Compare commits
45 Commits
WA2DE
...
28be43db02
| Author | SHA1 | Date | |
|---|---|---|---|
| 28be43db02 | |||
|
55a24fbaf0
|
|||
| 3b170324f4 | |||
|
8ccc7e263c
|
|||
|
b1e25a7d05
|
|||
|
3cb454d4aa
|
|||
|
3178bcbda9
|
|||
|
32d231cdaf
|
|||
|
526d036d75
|
|||
|
7a9d4178d9
|
|||
| 3e94d400e2 | |||
|
31fa9cd498
|
|||
| 676637fb1b | |||
|
9f3de4d924
|
|||
|
dafd5672bc
|
|||
|
70545ee0ad
|
|||
|
7917a7598b
|
|||
|
bb547a3347
|
|||
|
23d74c4643
|
|||
|
fcccbc15dd
|
|||
|
c68b75fcc1
|
|||
|
ab86dae90d
|
|||
|
ad72bb355b
|
|||
|
da357ac5ba
|
|||
|
833455803e
|
|||
| 74a9d29dc1 | |||
|
3615a1463c
|
|||
|
36ce6effe9
|
|||
|
250036f224
|
|||
|
b11620bbe8
|
|||
|
8a02a3a5cb
|
|||
|
7f6f49b3d0
|
|||
| 5f95117dd4 | |||
|
2f5bc10b8e
|
|||
|
257f519641
|
|||
|
5c5ecef3cf
|
|||
|
d0ebe596f6
|
|||
|
670b93d0a1
|
|||
|
306d3a4b55
|
|||
|
bf08b0de89
|
|||
|
b976db53c6
|
|||
|
be39d2dedb
|
|||
|
4ca770d16b
|
|||
|
6bf3b939bc
|
|||
| 7076efc2a1 |
@@ -1,4 +1,10 @@
|
||||
# .clang-format
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
BreakBeforeBraces: Linux
|
||||
ColumnLimit: 0
|
||||
FixNamespaceComments: false
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 120
|
||||
NamespaceIndentation: All
|
||||
TabWidth: 4
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -45,4 +45,5 @@ docs/man3
|
||||
docs/man
|
||||
docs/Doxyfile
|
||||
.cache
|
||||
|
||||
vcpkg_installed
|
||||
CMakeUserPresets.json
|
||||
|
||||
21
.gitmodules
vendored
21
.gitmodules
vendored
@@ -1,21 +0,0 @@
|
||||
[submodule "lib/json"]
|
||||
path = lib/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
master = master
|
||||
update = merge
|
||||
[submodule "lib/folding"]
|
||||
path = lib/folding
|
||||
url = https://github.com/rmontanana/folding
|
||||
main = main
|
||||
update = merge
|
||||
[submodule "tests/lib/catch2"]
|
||||
path = tests/lib/catch2
|
||||
url = https://github.com/catchorg/Catch2.git
|
||||
main = main
|
||||
update = merge
|
||||
[submodule "tests/lib/Files"]
|
||||
path = tests/lib/Files
|
||||
url = https://github.com/rmontanana/ArffFiles
|
||||
[submodule "lib/mdlp"]
|
||||
path = lib/mdlp
|
||||
url = https://github.com/rmontanana/mdlp
|
||||
36
CHANGELOG.md
36
CHANGELOG.md
@@ -7,8 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.0.7] 2025-03-16
|
||||
## [1.2.0] - 2025-06-30
|
||||
|
||||
### Internal
|
||||
|
||||
- Add docs generation to CMakeLists.txt.
|
||||
- Add new hyperparameters to the Ld classifiers:
|
||||
- *ld_algorithm*: algorithm to use for local discretization, with the following options: "MDLP", "BINQ", "BINU".
|
||||
- *ld_proposed_cuts*: number of cut points to return.
|
||||
- *mdlp_min_length*: minimum length of a partition in MDLP algorithm to be evaluated for partition.
|
||||
- *mdlp_max_depth*: maximum level of recursion in MDLP algorithm.
|
||||
- Remove vcpkg as a dependency manager, now the library is built with Conan package manager and CMake.
|
||||
|
||||
## [1.1.1] - 2025-05-20
|
||||
|
||||
### Internal
|
||||
|
||||
- Fix CFS metric expression in the FeatureSelection class.
|
||||
- Fix the vcpkg configuration in building the library.
|
||||
- Fix the sample app to use the vcpkg configuration.
|
||||
- Refactor the computeCPT method in the Node class with libtorch vectorized operations.
|
||||
- Refactor the sample to use local discretization models.
|
||||
|
||||
### Added
|
||||
|
||||
- Add predict_proba method to all Ld classifiers.
|
||||
- Add L1FS feature selection methods to the FeatureSelection class.
|
||||
|
||||
## [1.1.0] - 2025-04-27
|
||||
|
||||
### Internal
|
||||
|
||||
- Add changes to .clang-format to adjust to vscode format style thanks to <https://clang-format-configurator.site/>
|
||||
- Remove all the dependencies as git submodules and add them as vcpkg dependencies.
|
||||
- Fix the dependencies versions for this specific BayesNet version.
|
||||
|
||||
## [1.0.7] 2025-03-16
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
176
CLAUDE.md
Normal file
176
CLAUDE.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
BayesNet is a C++ library implementing Bayesian Network Classifiers. It provides various algorithms for machine learning classification including TAN, KDB, SPODE, SPnDE, AODE, A2DE, and their ensemble variants (Boost, XB). The library also includes local discretization variants (Ld) and feature selection algorithms.
|
||||
|
||||
## Build System & Dependencies
|
||||
|
||||
### Dependency Management
|
||||
The project supports **two package managers**:
|
||||
|
||||
#### vcpkg (Default)
|
||||
- Uses vcpkg with private registry at https://github.com/rmontanana/vcpkg-stash
|
||||
- Core dependencies: libtorch, nlohmann-json, folding, fimdlp, arff-files, catch2
|
||||
- All dependencies defined in `vcpkg.json` with version overrides
|
||||
|
||||
#### Conan (Alternative)
|
||||
- Modern C++ package manager with better dependency resolution
|
||||
- Configured via `conanfile.py` for packaging and distribution
|
||||
- Supports subset of dependencies (libtorch, nlohmann-json, catch2)
|
||||
- Custom dependencies (folding, fimdlp, arff-files) need custom Conan recipes
|
||||
|
||||
### Build Commands
|
||||
|
||||
#### Using vcpkg (Default)
|
||||
```bash
|
||||
# Initialize dependencies
|
||||
make init
|
||||
|
||||
# Build debug version (with tests and coverage)
|
||||
make debug
|
||||
make buildd
|
||||
|
||||
# Build release version
|
||||
make release
|
||||
make buildr
|
||||
|
||||
# Run tests
|
||||
make test
|
||||
|
||||
# Generate coverage report
|
||||
make coverage
|
||||
make viewcoverage
|
||||
|
||||
# Clean project
|
||||
make clean
|
||||
```
|
||||
|
||||
#### Using Conan
|
||||
```bash
|
||||
# Install Conan first: pip install conan
|
||||
|
||||
# Initialize dependencies
|
||||
make conan-init
|
||||
|
||||
# Build debug version (with tests and coverage)
|
||||
make conan-debug
|
||||
make buildd
|
||||
|
||||
# Build release version
|
||||
make conan-release
|
||||
make buildr
|
||||
|
||||
# Create and test Conan package
|
||||
make conan-create
|
||||
|
||||
# Upload to Conan remote
|
||||
make conan-upload remote=myremote
|
||||
|
||||
# Clean Conan cache and builds
|
||||
make conan-clean
|
||||
```
|
||||
|
||||
### CMake Configuration
|
||||
- Uses CMake 3.27+ with C++17 standard
|
||||
- Debug builds automatically enable testing and coverage
|
||||
- Release builds optimize with `-Ofast`
|
||||
- **Automatic package manager detection**: CMake detects whether Conan or vcpkg is being used
|
||||
- Supports both static library and package manager installation
|
||||
- Conditional dependency linking based on availability
|
||||
|
||||
## Testing Framework
|
||||
|
||||
- **Catch2** testing framework (version 3.8.1)
|
||||
- Test executable: `TestBayesNet` in `build_Debug/tests/`
|
||||
- Individual test categories can be run: `./TestBayesNet "[CategoryName]"`
|
||||
- Coverage reporting with lcov/genhtml
|
||||
|
||||
### Test Categories
|
||||
- A2DE, BoostA2DE, BoostAODE, XSPODE, XSPnDE, XBAODE, XBA2DE
|
||||
- Classifier, Ensemble, FeatureSelection, Metrics, Models
|
||||
- Network, Node, MST, Modules
|
||||
|
||||
## Code Architecture
|
||||
|
||||
### Core Structure
|
||||
```
|
||||
bayesnet/
|
||||
├── BaseClassifier.h # Abstract base for all classifiers
|
||||
├── classifiers/ # Basic Bayesian classifiers (TAN, KDB, SPODE, etc.)
|
||||
├── ensembles/ # Ensemble methods (AODE, A2DE, Boost variants)
|
||||
├── feature_selection/ # Feature selection algorithms (CFS, FCBF, IWSS, L1FS)
|
||||
├── network/ # Bayesian network structure (Network, Node)
|
||||
└── utils/ # Utilities (metrics, MST, tensor operations)
|
||||
```
|
||||
|
||||
### Key Design Patterns
|
||||
- **BaseClassifier** abstract interface for all algorithms
|
||||
- Template-based design with both std::vector and torch::Tensor support
|
||||
- Network/Node abstraction for Bayesian network representation
|
||||
- Feature selection as separate, composable modules
|
||||
|
||||
### Data Handling
|
||||
- Supports both discrete integer data and continuous data with discretization
|
||||
- ARFF file format support through arff-files library
|
||||
- Tensor operations via PyTorch C++ (libtorch)
|
||||
- Local discretization variants use fimdlp library
|
||||
|
||||
## Documentation & Tools
|
||||
|
||||
- **Doxygen** for API documentation: `make doc`
|
||||
- **lcov** for coverage reports: `make coverage`
|
||||
- **plantuml + clang-uml** for UML diagrams: `make diagrams`
|
||||
- Man pages available in `docs/man3/`
|
||||
|
||||
## Sample Applications
|
||||
|
||||
Sample code in `sample/` directory demonstrates library usage:
|
||||
```bash
|
||||
make sample fname=tests/data/iris.arff model=TANLd
|
||||
```
|
||||
|
||||
## Package Distribution
|
||||
|
||||
### Creating Conan Packages
|
||||
```bash
|
||||
# Create package locally
|
||||
make conan-create
|
||||
|
||||
# Test package installation
|
||||
cd test_package
|
||||
conan create ..
|
||||
|
||||
# Upload to remote repository
|
||||
make conan-upload remote=myremote profile=myprofile
|
||||
```
|
||||
|
||||
### Using the Library
|
||||
With Conan:
|
||||
```python
|
||||
# conanfile.txt or conanfile.py
|
||||
[requires]
|
||||
bayesnet/1.1.2@user/channel
|
||||
|
||||
[generators]
|
||||
cmake
|
||||
```
|
||||
|
||||
With vcpkg:
|
||||
```json
|
||||
{
|
||||
"dependencies": ["bayesnet"]
|
||||
}
|
||||
```
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
- **Add new classifier**: Extend BaseClassifier, implement in appropriate subdirectory
|
||||
- **Add new test**: Update `tests/CMakeLists.txt` and create test in `tests/`
|
||||
- **Modify build**: Edit main `CMakeLists.txt` or use Makefile targets
|
||||
- **Update dependencies**:
|
||||
- vcpkg: Modify `vcpkg.json` and run `make init`
|
||||
- Conan: Modify `conanfile.py` and run `make conan-init`
|
||||
- **Package for distribution**: Use `make conan-create` for Conan packaging
|
||||
133
CMakeLists.txt
133
CMakeLists.txt
@@ -1,21 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_minimum_required(VERSION 3.27)
|
||||
|
||||
project(BayesNet
|
||||
VERSION 1.0.7
|
||||
project(bayesnet
|
||||
VERSION 1.2.0
|
||||
DESCRIPTION "Bayesian Network and basic classifiers Library."
|
||||
HOMEPAGE_URL "https://github.com/rmontanana/bayesnet"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
if (CODE_COVERAGE AND NOT ENABLE_TESTING)
|
||||
MESSAGE(FATAL_ERROR "Code coverage requires testing enabled")
|
||||
endif (CODE_COVERAGE AND NOT ENABLE_TESTING)
|
||||
|
||||
find_package(Torch REQUIRED)
|
||||
|
||||
if (POLICY CMP0135)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
endif ()
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
|
||||
# Global CMake variables
|
||||
# ----------------------
|
||||
@@ -26,72 +19,108 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage -fno-elide-constructors")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-default-inline")
|
||||
endif()
|
||||
|
||||
# Options
|
||||
# -------
|
||||
option(ENABLE_CLANG_TIDY "Enable to add clang tidy." OFF)
|
||||
option(ENABLE_TESTING "Unit testing build" OFF)
|
||||
option(CODE_COVERAGE "Collect coverage from test library" OFF)
|
||||
option(INSTALL_GTEST "Enable installation of googletest." OFF)
|
||||
|
||||
# CMakes modules
|
||||
# --------------
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
|
||||
include(AddGitSubmodule)
|
||||
|
||||
find_package(Torch CONFIG REQUIRED)
|
||||
if(NOT TARGET torch::torch)
|
||||
add_library(torch::torch INTERFACE IMPORTED GLOBAL)
|
||||
# expose include paths and libraries that the find-module discovered
|
||||
set_target_properties(torch::torch PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${TORCH_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES "${TORCH_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
find_package(fimdlp CONFIG REQUIRED)
|
||||
find_package(folding CONFIG REQUIRED)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
|
||||
add_subdirectory(config)
|
||||
|
||||
# Add the library
|
||||
# ---------------
|
||||
include_directories(
|
||||
${bayesnet_SOURCE_DIR}
|
||||
${CMAKE_BINARY_DIR}/configured_files/include
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE Sources "bayesnet/*.cc")
|
||||
|
||||
add_library(bayesnet ${Sources})
|
||||
|
||||
target_link_libraries(bayesnet
|
||||
nlohmann_json::nlohmann_json
|
||||
folding::folding
|
||||
fimdlp::fimdlp
|
||||
torch::torch
|
||||
arff-files::arff-files
|
||||
)
|
||||
|
||||
# Testing
|
||||
# -------
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
MESSAGE("Debug mode")
|
||||
set(ENABLE_TESTING ON)
|
||||
set(CODE_COVERAGE ON)
|
||||
endif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
|
||||
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
|
||||
message(STATUS "Languages=${LANGUAGES}")
|
||||
if (CODE_COVERAGE)
|
||||
enable_testing()
|
||||
include(CodeCoverage)
|
||||
MESSAGE(STATUS "Code coverage enabled")
|
||||
SET(GCC_COVERAGE_LINK_FLAGS " ${GCC_COVERAGE_LINK_FLAGS} -lgcov --coverage")
|
||||
endif (CODE_COVERAGE)
|
||||
|
||||
if (ENABLE_CLANG_TIDY)
|
||||
include(StaticAnalyzers) # clang-tidy
|
||||
endif (ENABLE_CLANG_TIDY)
|
||||
|
||||
# External libraries - dependencies of BayesNet
|
||||
# ---------------------------------------------
|
||||
|
||||
# include(FetchContent)
|
||||
add_git_submodule("lib/json")
|
||||
add_git_submodule("lib/mdlp")
|
||||
|
||||
# Subdirectories
|
||||
# --------------
|
||||
add_subdirectory(config)
|
||||
add_subdirectory(bayesnet)
|
||||
|
||||
# Testing
|
||||
# -------
|
||||
if (ENABLE_TESTING)
|
||||
MESSAGE(STATUS "Testing enabled")
|
||||
add_subdirectory(tests/lib/catch2)
|
||||
MESSAGE(STATUS "Testing enabled")
|
||||
find_package(Catch2 CONFIG REQUIRED)
|
||||
find_package(arff-files CONFIG REQUIRED)
|
||||
enable_testing()
|
||||
include(CTest)
|
||||
add_subdirectory(tests)
|
||||
else(ENABLE_TESTING)
|
||||
message("Release mode")
|
||||
endif (ENABLE_TESTING)
|
||||
|
||||
# Installation
|
||||
# ------------
|
||||
install(TARGETS BayesNet
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/bayesnetConfigVersion.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bayesnetConfig.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/bayesnetConfig.cmake"
|
||||
INSTALL_DESTINATION share/bayesnet)
|
||||
|
||||
install(TARGETS bayesnet
|
||||
EXPORT bayesnetTargets
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
CONFIGURATIONS Release)
|
||||
install(DIRECTORY bayesnet/ DESTINATION include/bayesnet FILES_MATCHING CONFIGURATIONS Release PATTERN "*.h")
|
||||
install(FILES ${CMAKE_BINARY_DIR}/configured_files/include/bayesnet/config.h DESTINATION include/bayesnet CONFIGURATIONS Release)
|
||||
|
||||
install(DIRECTORY bayesnet/
|
||||
DESTINATION include/bayesnet
|
||||
FILES_MATCHING
|
||||
CONFIGURATIONS Release
|
||||
PATTERN "*.h")
|
||||
install(FILES ${CMAKE_BINARY_DIR}/configured_files/include/bayesnet/config.h
|
||||
DESTINATION include/bayesnet
|
||||
CONFIGURATIONS Release)
|
||||
|
||||
install(EXPORT bayesnetTargets
|
||||
FILE bayesnetTargets.cmake
|
||||
NAMESPACE bayesnet::
|
||||
DESTINATION share/bayesnet)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/bayesnetConfig.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/bayesnetConfigVersion.cmake"
|
||||
DESTINATION share/bayesnet
|
||||
)
|
||||
# Documentation
|
||||
# -------------
|
||||
find_package(Doxygen)
|
||||
|
||||
87
CONAN_README.md
Normal file
87
CONAN_README.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Using BayesNet with Conan
|
||||
|
||||
This document explains how to use Conan as an alternative package manager for BayesNet.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
pip install conan
|
||||
conan remote add Cimmeria https://conan.rmontanana.es/artifactory/api/conan/Cimmeria
|
||||
conan profile new default --detect
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### As a Consumer
|
||||
|
||||
1. Create a `conanfile.txt` in your project:
|
||||
|
||||
```ini
|
||||
[requires]
|
||||
libtorch/2.7.0
|
||||
bayesnet/1.2.0
|
||||
|
||||
[generators]
|
||||
CMakeDeps
|
||||
CMakeToolchain
|
||||
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
|
||||
```bash
|
||||
conan install . --build=missing
|
||||
```
|
||||
|
||||
3. In your CMakeLists.txt:
|
||||
|
||||
```cmake
|
||||
find_package(bayesnet REQUIRED)
|
||||
target_link_libraries(your_target bayesnet::bayesnet)
|
||||
```
|
||||
|
||||
### Building BayesNet with Conan
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
make conan-init
|
||||
|
||||
# Build debug version
|
||||
make debug
|
||||
make buildd
|
||||
|
||||
# Build release version
|
||||
make release
|
||||
make buildr
|
||||
|
||||
# Create package
|
||||
make conan-create
|
||||
```
|
||||
|
||||
## Current Limitations
|
||||
|
||||
- Custom dependencies (folding, fimdlp, arff-files) are not available in ConanCenter
|
||||
- These need to be built as custom Conan packages or replaced with alternatives
|
||||
- The conanfile.py currently comments out these dependencies
|
||||
|
||||
## Creating Custom Dependency Packages
|
||||
|
||||
For the custom dependencies, you'll need to create Conan recipes:
|
||||
|
||||
1. **folding**: Cross-validation library
|
||||
2. **fimdlp**: Discretization library
|
||||
3. **arff-files**: ARFF file format parser
|
||||
|
||||
Contact the maintainer or create custom recipes for these packages.
|
||||
|
||||
## Package Distribution
|
||||
|
||||
Once custom dependencies are resolved:
|
||||
|
||||
```bash
|
||||
# Create and test package
|
||||
make conan-create
|
||||
|
||||
# Upload to your remote
|
||||
conan upload bayesnet/1.2.0 -r myremote
|
||||
```
|
||||
158
Makefile
158
Makefile
@@ -1,11 +1,11 @@
|
||||
SHELL := /bin/bash
|
||||
.DEFAULT_GOAL := help
|
||||
.PHONY: viewcoverage coverage setup help install uninstall diagrams buildr buildd test clean debug release sample updatebadge doc doc-install
|
||||
.PHONY: viewcoverage coverage setup help install uninstall diagrams buildr buildd test clean updatebadge doc doc-install init clean-test debug release conan-create conan-upload conan-clean sample
|
||||
|
||||
f_release = build_Release
|
||||
f_debug = build_Debug
|
||||
f_diagrams = diagrams
|
||||
app_targets = BayesNet
|
||||
app_targets = bayesnet
|
||||
test_targets = TestBayesNet
|
||||
clang-uml = clang-uml
|
||||
plantuml = plantuml
|
||||
@@ -31,7 +31,6 @@ define ClearTests
|
||||
fi ;
|
||||
endef
|
||||
|
||||
|
||||
setup: ## Install dependencies for tests and coverage
|
||||
@if [ "$(shell uname)" = "Darwin" ]; then \
|
||||
brew install gcovr; \
|
||||
@@ -43,30 +42,30 @@ setup: ## Install dependencies for tests and coverage
|
||||
fi
|
||||
@echo "* You should install plantuml & graphviz for the diagrams"
|
||||
|
||||
diagrams: ## Create an UML class diagram & dependency of the project (diagrams/BayesNet.png)
|
||||
@which $(plantuml) || (echo ">>> Please install plantuml"; exit 1)
|
||||
@which $(dot) || (echo ">>> Please install graphviz"; exit 1)
|
||||
@which $(clang-uml) || (echo ">>> Please install clang-uml"; exit 1)
|
||||
@export PLANTUML_LIMIT_SIZE=16384
|
||||
@echo ">>> Creating UML class diagram of the project...";
|
||||
@$(clang-uml) -p
|
||||
@cd $(f_diagrams); \
|
||||
$(plantuml) -tsvg BayesNet.puml
|
||||
@echo ">>> Creating dependency graph diagram of the project...";
|
||||
$(MAKE) debug
|
||||
cd $(f_debug) && cmake .. --graphviz=dependency.dot
|
||||
@$(dot) -Tsvg $(f_debug)/dependency.dot.BayesNet -o $(f_diagrams)/dependency.svg
|
||||
clean: ## Clean the project
|
||||
@echo ">>> Cleaning the project..."
|
||||
@if test -f CMakeCache.txt ; then echo "- Deleting CMakeCache.txt"; rm -f CMakeCache.txt; fimake
|
||||
@for folder in $(f_release) $(f_debug) vpcpkg_installed install_test ; do \
|
||||
if test -d "$$folder" ; then \
|
||||
echo "- Deleting $$folder folder" ; \
|
||||
rm -rf "$$folder"; \
|
||||
fi; \
|
||||
done
|
||||
@$(MAKE) clean-test
|
||||
@echo ">>> Done";
|
||||
|
||||
# Build targets
|
||||
# =============
|
||||
|
||||
buildd: ## Build the debug targets
|
||||
cmake --build $(f_debug) -t $(app_targets) --parallel $(CMAKE_BUILD_PARALLEL_LEVEL)
|
||||
cmake --build $(f_debug) --config Debug -t $(app_targets) --parallel $(CMAKE_BUILD_PARALLEL_LEVEL)
|
||||
|
||||
buildr: ## Build the release targets
|
||||
cmake --build $(f_release) -t $(app_targets) --parallel $(CMAKE_BUILD_PARALLEL_LEVEL)
|
||||
cmake --build $(f_release) --config Release -t $(app_targets) --parallel $(CMAKE_BUILD_PARALLEL_LEVEL)
|
||||
|
||||
clean: ## Clean the tests info
|
||||
@echo ">>> Cleaning Debug BayesNet tests...";
|
||||
$(call ClearTests)
|
||||
@echo ">>> Done";
|
||||
|
||||
# Install targets
|
||||
# ===============
|
||||
|
||||
uninstall: ## Uninstall library
|
||||
@echo ">>> Uninstalling BayesNet...";
|
||||
@@ -79,40 +78,19 @@ install: ## Install library
|
||||
@cmake --install $(f_release) --prefix $(prefix)
|
||||
@echo ">>> Done";
|
||||
|
||||
debug: ## Build a debug version of the project
|
||||
@echo ">>> Building Debug BayesNet...";
|
||||
@if [ -d ./$(f_debug) ]; then rm -rf ./$(f_debug); fi
|
||||
@mkdir $(f_debug);
|
||||
@cmake -S . -B $(f_debug) -D CMAKE_BUILD_TYPE=Debug -D ENABLE_TESTING=ON -D CODE_COVERAGE=ON
|
||||
@echo ">>> Done";
|
||||
|
||||
release: ## Build a Release version of the project
|
||||
@echo ">>> Building Release BayesNet...";
|
||||
@if [ -d ./$(f_release) ]; then rm -rf ./$(f_release); fi
|
||||
@mkdir $(f_release);
|
||||
@cmake -S . -B $(f_release) -D CMAKE_BUILD_TYPE=Release
|
||||
@echo ">>> Done";
|
||||
# Test targets
|
||||
# ============
|
||||
|
||||
fname = "tests/data/iris.arff"
|
||||
sample: ## Build sample
|
||||
@echo ">>> Building Sample...";
|
||||
@if [ -d ./sample/build ]; then rm -rf ./sample/build; fi
|
||||
@cd sample && cmake -B build -S . -D CMAKE_BUILD_TYPE=Debug && cmake --build build -t bayesnet_sample
|
||||
sample/build/bayesnet_sample $(fname)
|
||||
@echo ">>> Done";
|
||||
|
||||
fname = "tests/data/iris.arff"
|
||||
sample2: ## Build sample2
|
||||
@echo ">>> Building Sample...";
|
||||
@if [ -d ./sample/build ]; then rm -rf ./sample/build; fi
|
||||
@cd sample && cmake -B build -S . -D CMAKE_BUILD_TYPE=Debug && cmake --build build -t bayesnet_sample_xspode
|
||||
sample/build/bayesnet_sample_xspode $(fname)
|
||||
clean-test: ## Clean the tests info
|
||||
@echo ">>> Cleaning Debug BayesNet tests...";
|
||||
$(call ClearTests)
|
||||
@echo ">>> Done";
|
||||
|
||||
opt = ""
|
||||
test: ## Run tests (opt="-s") to verbose output the tests, (opt="-c='Test Maximum Spanning Tree'") to run only that section
|
||||
@echo ">>> Running BayesNet tests...";
|
||||
@$(MAKE) clean
|
||||
@$(MAKE) clean-test
|
||||
@cmake --build $(f_debug) -t $(test_targets) --parallel $(CMAKE_BUILD_PARALLEL_LEVEL)
|
||||
@for t in $(test_targets); do \
|
||||
echo ">>> Running $$t...";\
|
||||
@@ -133,10 +111,12 @@ coverage: ## Run tests and generate coverage report (build/index.html)
|
||||
$(lcov) --directory CMakeFiles --capture --demangle-cpp --ignore-errors source,source --output-file coverage.info >/dev/null 2>&1; \
|
||||
$(lcov) --remove coverage.info '/usr/*' --output-file coverage.info >/dev/null 2>&1; \
|
||||
$(lcov) --remove coverage.info 'lib/*' --output-file coverage.info >/dev/null 2>&1; \
|
||||
$(lcov) --remove coverage.info 'include/*' --output-file coverage.info >/dev/null 2>&1; \
|
||||
$(lcov) --remove coverage.info 'libtorch/*' --output-file coverage.info >/dev/null 2>&1; \
|
||||
$(lcov) --remove coverage.info 'tests/*' --output-file coverage.info >/dev/null 2>&1; \
|
||||
$(lcov) --remove coverage.info 'bayesnet/utils/loguru.*' --ignore-errors unused --output-file coverage.info >/dev/null 2>&1; \
|
||||
$(lcov) --remove coverage.info '/opt/miniconda/*' --ignore-errors unused --output-file coverage.info >/dev/null 2>&1; \
|
||||
$(lcov) --remove coverage.info '*/.conan2/*' --ignore-errors unused --output-file coverage.info >/dev/null 2>&1; \
|
||||
$(lcov) --summary coverage.info
|
||||
@$(MAKE) updatebadge
|
||||
@echo ">>> Done";
|
||||
@@ -162,6 +142,9 @@ updatebadge: ## Update the coverage badge in README.md
|
||||
@env python update_coverage.py $(f_debug)/tests
|
||||
@echo ">>> Done";
|
||||
|
||||
# Documentation targets
|
||||
# =====================
|
||||
|
||||
doc: ## Generate documentation
|
||||
@echo ">>> Generating documentation..."
|
||||
@cmake --build $(f_release) -t doxygen
|
||||
@@ -176,6 +159,20 @@ doc: ## Generate documentation
|
||||
fi
|
||||
@echo ">>> Done";
|
||||
|
||||
diagrams: ## Create an UML class diagram & dependency of the project (diagrams/BayesNet.png)
|
||||
@which $(plantuml) || (echo ">>> Please install plantuml"; exit 1)
|
||||
@which $(dot) || (echo ">>> Please install graphviz"; exit 1)
|
||||
@which $(clang-uml) || (echo ">>> Please install clang-uml"; exit 1)
|
||||
@export PLANTUML_LIMIT_SIZE=16384
|
||||
@echo ">>> Creating UML class diagram of the project...";
|
||||
@$(clang-uml) -p
|
||||
@cd $(f_diagrams); \
|
||||
$(plantuml) -tsvg BayesNet.puml
|
||||
@echo ">>> Creating dependency graph diagram of the project...";
|
||||
$(MAKE) debug
|
||||
cd $(f_debug) && cmake .. --graphviz=dependency.dot
|
||||
@$(dot) -Tsvg $(f_debug)/dependency.dot.BayesNet -o $(f_diagrams)/dependency.svg
|
||||
|
||||
docdir = ""
|
||||
doc-install: ## Install documentation
|
||||
@echo ">>> Installing documentation..."
|
||||
@@ -190,6 +187,69 @@ doc-install: ## Install documentation
|
||||
@sudo cp -rp $(mansrcdir) $(mandestdir)
|
||||
@echo ">>> Done";
|
||||
|
||||
# Conan package manager targets
|
||||
# =============================
|
||||
|
||||
debug: ## Build debug version using Conan
|
||||
@echo ">>> Building *Debug* BayesNet with Conan..."
|
||||
@rm -rf $(f_debug) # wipe previous tree
|
||||
@conan install . \
|
||||
-s build_type=Debug \
|
||||
--build=missing \
|
||||
-of $(f_debug)
|
||||
@cmake -S . -B $(f_debug) \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DENABLE_TESTING=ON \
|
||||
-DCODE_COVERAGE=ON \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$(f_debug)/build/Debug/generators/conan_toolchain.cmake
|
||||
@echo ">>> Done"
|
||||
|
||||
release: ## Build release version using Conan
|
||||
@echo ">>> Building Release BayesNet with Conan..."
|
||||
@conan install . \
|
||||
-s build_type=Release \
|
||||
--build=missing \
|
||||
-of $(f_debug)
|
||||
@if [ -d ./$(f_release) ]; then rm -rf ./$(f_release); fi
|
||||
@mkdir $(f_release)
|
||||
@conan install . -s build_type=Release --build=missing -of $(f_release)
|
||||
@cmake -S . -B $(f_release) -D CMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=$(f_release)/build/Release/generators/conan_toolchain.cmake
|
||||
@echo ">>> Done"
|
||||
|
||||
conan-create: ## Create Conan package
|
||||
@echo ">>> Creating Conan package..."
|
||||
@conan create . --build=missing -tf "" --profile=release -tf ""
|
||||
@conan create . --build=missing -tf "" --profile=debug
|
||||
@echo ">>> Done"
|
||||
|
||||
profile ?= release
|
||||
remote ?= Cimmeria
|
||||
conan-upload: ## Upload package to Conan remote (profile=release remote=Cimmeria)
|
||||
@echo ">>> Uploading to Conan remote $(remote) with profile $(profile)..."
|
||||
@conan upload bayesnet/$(grep version conanfile.py | cut -d'"' -f2) -r $(remote) --confirm
|
||||
@echo ">>> Done"
|
||||
|
||||
conan-clean: ## Clean Conan cache and build folders
|
||||
@echo ">>> Cleaning Conan cache and build folders..."
|
||||
@conan remove "*" --confirm
|
||||
@if test -d "$(f_release)" ; then rm -rf "$(f_release)"; fi
|
||||
@if test -d "$(f_debug)" ; then rm -rf "$(f_debug)"; fi
|
||||
@echo ">>> Done"
|
||||
|
||||
fname = "tests/data/iris.arff"
|
||||
model = "TANLd"
|
||||
sample: ## Build sample with Conan
|
||||
@echo ">>> Building Sample with Conan...";
|
||||
@if [ -d ./sample/build ]; then rm -rf ./sample/build; fi
|
||||
@cd sample && conan install . --output-folder=build --build=missing
|
||||
@cd sample && cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake && \
|
||||
cmake --build build -t bayesnet_sample
|
||||
sample/build/bayesnet_sample $(fname) $(model)
|
||||
@echo ">>> Done";
|
||||
|
||||
# Help target
|
||||
# ===========
|
||||
|
||||
help: ## Show help message
|
||||
@IFS=$$'\n' ; \
|
||||
help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##/:/'`); \
|
||||
|
||||
113
README.md
113
README.md
@@ -2,64 +2,125 @@
|
||||
|
||||

|
||||
[](<https://opensource.org/licenses/MIT>)
|
||||

|
||||

|
||||
[](https://app.codacy.com/gh/Doctorado-ML/BayesNet/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
||||
[](https://sonarcloud.io/summary/new_code?id=rmontanana_BayesNet)
|
||||
[](https://sonarcloud.io/summary/new_code?id=rmontanana_BayesNet)
|
||||

|
||||
[](html/index.html)
|
||||
[](https://deepwiki.com/Doctorado-ML/BayesNet)
|
||||

|
||||
[](https://gitea.rmontanana.es/rmontanana/BayesNet)
|
||||
[](https://doi.org/10.5281/zenodo.14210344)
|
||||
|
||||
Bayesian Network Classifiers library
|
||||
|
||||
## Dependencies
|
||||
## Using the Library
|
||||
|
||||
The only external dependency is [libtorch](https://pytorch.org/cppdocs/installing.html) which can be installed with the following commands:
|
||||
### Using Conan Package Manager
|
||||
|
||||
```bash
|
||||
wget https://download.pytorch.org/libtorch/nightly/cpu/libtorch-shared-with-deps-latest.zip
|
||||
unzip libtorch-shared-with-deps-latest.zip
|
||||
You can use the library with the [Conan](https://conan.io/) package manager. In your project you need to add the following files:
|
||||
|
||||
#### conanfile.txt
|
||||
|
||||
```txt
|
||||
[requires]
|
||||
bayesnet/1.1.2
|
||||
|
||||
[generators]
|
||||
CMakeDeps
|
||||
CMakeToolchain
|
||||
```
|
||||
|
||||
## Setup
|
||||
#### CMakeLists.txt
|
||||
|
||||
Include the following lines in your `CMakeLists.txt` file:
|
||||
|
||||
```cmake
|
||||
find_package(bayesnet REQUIRED)
|
||||
|
||||
add_executable(myapp main.cpp)
|
||||
|
||||
target_link_libraries(myapp PRIVATE bayesnet::bayesnet)
|
||||
```
|
||||
|
||||
Then install the dependencies and build your project:
|
||||
|
||||
```bash
|
||||
conan install . --output-folder=build --build=missing
|
||||
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
**Note: In the `sample` folder you can find a sample application that uses the library. You can use it as a reference to create your own application.**
|
||||
|
||||
## Building and Testing
|
||||
|
||||
The project uses [Conan](https://conan.io/) for dependency management and provides convenient Makefile targets for common tasks.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Conan](https://conan.io/) package manager (`pip install conan`)
|
||||
- CMake 3.27+
|
||||
- C++17 compatible compiler
|
||||
|
||||
### Getting the code
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/doctorado-ml/bayesnet
|
||||
git clone https://github.com/doctorado-ml/bayesnet
|
||||
cd bayesnet
|
||||
```
|
||||
|
||||
### Release
|
||||
### Build Commands
|
||||
|
||||
#### Release Build
|
||||
|
||||
```bash
|
||||
make release
|
||||
make buildr
|
||||
sudo make install
|
||||
make release # Configure release build with Conan
|
||||
make buildr # Build the release version
|
||||
```
|
||||
|
||||
### Debug & Tests
|
||||
#### Debug Build & Tests
|
||||
|
||||
```bash
|
||||
make debug
|
||||
make test
|
||||
make debug # Configure debug build with Conan
|
||||
make buildd # Build the debug version
|
||||
make test # Run the tests
|
||||
```
|
||||
|
||||
### Coverage
|
||||
#### Coverage Analysis
|
||||
|
||||
```bash
|
||||
make coverage
|
||||
make viewcoverage
|
||||
make coverage # Run tests with coverage analysis
|
||||
make viewcoverage # View coverage report in browser
|
||||
```
|
||||
|
||||
### Sample app
|
||||
#### Sample Application
|
||||
|
||||
After building and installing the release version, you can run the sample app with the following commands:
|
||||
Run the sample application with different datasets and models:
|
||||
|
||||
```bash
|
||||
make sample
|
||||
make sample fname=tests/data/glass.arff
|
||||
make sample # Run with default settings
|
||||
make sample fname=tests/data/glass.arff # Use glass dataset
|
||||
make sample fname=tests/data/iris.arff model=AODE # Use specific model
|
||||
```
|
||||
|
||||
### Available Makefile Targets
|
||||
|
||||
- `debug` - Configure debug build using Conan
|
||||
- `release` - Configure release build using Conan
|
||||
- `buildd` - Build debug targets
|
||||
- `buildr` - Build release targets
|
||||
- `test` - Run all tests (use `opt="-s"` for verbose output)
|
||||
- `coverage` - Generate test coverage report
|
||||
- `viewcoverage` - Open coverage report in browser
|
||||
- `sample` - Build and run sample application
|
||||
- `conan-create` - Create Conan package
|
||||
- `conan-upload` - Upload package to Conan remote
|
||||
- `conan-clean` - Clean Conan cache and build folders
|
||||
- `clean` - Clean all build artifacts
|
||||
- `doc` - Generate documentation
|
||||
- `diagrams` - Generate UML diagrams
|
||||
- `help` - Show all available targets
|
||||
|
||||
## Models
|
||||
|
||||
#### - TAN
|
||||
@@ -76,8 +137,12 @@ make sample fname=tests/data/glass.arff
|
||||
|
||||
#### - [BoostAODE](docs/BoostAODE.md)
|
||||
|
||||
#### - XBAODE
|
||||
|
||||
#### - BoostA2DE
|
||||
|
||||
#### - XBA2DE
|
||||
|
||||
### With Local Discretization
|
||||
|
||||
#### - TANLd
|
||||
|
||||
518
REVISION_TECNICA_BAYESNET.md
Normal file
518
REVISION_TECNICA_BAYESNET.md
Normal file
@@ -0,0 +1,518 @@
|
||||
# Revisión Técnica de BayesNet - Informe Completo
|
||||
|
||||
## Resumen Ejecutivo
|
||||
|
||||
Como desarrollador experto en C++, he realizado una revisión técnica exhaustiva de la biblioteca BayesNet, evaluando su arquitectura, calidad de código, rendimiento y mantenibilidad. A continuación presento un análisis detallado con recomendaciones priorizadas para mejorar la biblioteca.
|
||||
|
||||
## 1. Fortalezas Identificadas
|
||||
|
||||
### 1.1 Arquitectura y Diseño
|
||||
- **✅ Diseño orientado a objetos bien estructurado** con jerarquía clara de clases
|
||||
- **✅ Uso adecuado de smart pointers** (std::unique_ptr) en la mayoría del código
|
||||
- **✅ Abstracción coherente** a través de BaseClassifier
|
||||
- **✅ Separación clara de responsabilidades** entre módulos
|
||||
- **✅ Documentación API con Doxygen** completa y actualizada
|
||||
|
||||
### 1.2 Gestión de Dependencias y Build
|
||||
- **✅ Sistema vcpkg** bien configurado para gestión de dependencias
|
||||
- **✅ CMake moderno** (3.27+) con configuración robusta
|
||||
- **✅ Separación Debug/Release** con optimizaciones apropiadas
|
||||
- **✅ Sistema de testing integrado** con Catch2
|
||||
|
||||
### 1.3 Testing y Cobertura
|
||||
- **✅ 17 archivos de test** cubriendo los componentes principales
|
||||
- **✅ Tests parametrizados** con múltiples datasets
|
||||
- **✅ Integración con lcov** para reportes de cobertura
|
||||
- **✅ Tests automáticos** en el proceso de build
|
||||
|
||||
## 2. Debilidades y Problemas Críticos
|
||||
|
||||
### 2.1 Problemas de Gestión de Memoria
|
||||
|
||||
#### **🔴 CRÍTICO: Memory Leak Potencial**
|
||||
**Archivo:** `/bayesnet/ensembles/Boost.cc` (líneas 124-141)
|
||||
```cpp
|
||||
// PROBLEMA: Raw pointer sin RAII
|
||||
FeatureSelect* featureSelector = nullptr;
|
||||
if (select_features_algorithm == SelectFeatures.CFS) {
|
||||
featureSelector = new CFS(...); // ❌ Riesgo de leak
|
||||
}
|
||||
// ...
|
||||
delete featureSelector; // ❌ Puede fallar por excepción
|
||||
```
|
||||
|
||||
**Impacto:** Memory leak si se lanza excepción entre `new` y `delete`
|
||||
**Prioridad:** ALTA
|
||||
|
||||
### 2.2 Problemas de Performance
|
||||
|
||||
#### **🔴 CRÍTICO: Complejidad O(n³)**
|
||||
**Archivo:** `/bayesnet/utils/BayesMetrics.cc` (líneas 41-53)
|
||||
```cpp
|
||||
for (int i = 0; i < n - 1; ++i) {
|
||||
if (std::find(featuresExcluded.begin(), featuresExcluded.end(), i) != featuresExcluded.end()) {
|
||||
continue; // ❌ O(n) en bucle anidado
|
||||
}
|
||||
for (int j = i + 1; j < n; ++j) {
|
||||
if (std::find(featuresExcluded.begin(), featuresExcluded.end(), j) != featuresExcluded.end()) {
|
||||
continue; // ❌ O(n) en bucle anidado
|
||||
}
|
||||
// Más operaciones costosas...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Impacto:** Con 100 features = 1,250,000 operaciones de búsqueda
|
||||
**Prioridad:** ALTA
|
||||
|
||||
#### **🔴 CRÍTICO: Threading Ineficiente**
|
||||
**Archivo:** `/bayesnet/network/Network.cc` (líneas 269-273)
|
||||
```cpp
|
||||
for (int i = 0; i < samples.size(1); ++i) {
|
||||
threads.emplace_back(worker, sample, i); // ❌ Thread per sample
|
||||
}
|
||||
```
|
||||
|
||||
**Impacto:** Con 10,000 muestras = 10,000 threads (context switching excesivo)
|
||||
**Prioridad:** ALTA
|
||||
|
||||
### 2.3 Problemas de Calidad de Código
|
||||
|
||||
#### **🟡 MODERADO: Funciones Excesivamente Largas**
|
||||
- `XSP2DE.cc`: 575 líneas (violación de SRP)
|
||||
- `Boost::setHyperparameters()`: 150+ líneas
|
||||
- `L1FS::fitLasso()`: 200+ líneas de complejidad algoritmica alta
|
||||
|
||||
#### **🟡 MODERADO: Validación Insuficiente**
|
||||
```cpp
|
||||
// En múltiples archivos: falta validación de entrada
|
||||
if (features.empty()) {
|
||||
// Sin manejo de caso edge
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 Problemas de Algoritmos
|
||||
|
||||
#### **🟡 MODERADO: Union-Find Subóptimo**
|
||||
**Archivo:** `/bayesnet/utils/Mst.cc`
|
||||
```cpp
|
||||
// ❌ Sin compresión de caminos ni unión por rango
|
||||
int find_set(int i) {
|
||||
if (i != parent[i])
|
||||
i = find_set(parent[i]); // Ineficiente O(n)
|
||||
return i;
|
||||
}
|
||||
```
|
||||
|
||||
**Impacto:** Algoritmo MST subóptimo O(V²) en lugar de O(E log V)
|
||||
|
||||
## 3. Plan de Mejoras Priorizadas
|
||||
|
||||
### 3.1 Fase 1: Problemas Críticos (Semanas 1-2)
|
||||
|
||||
#### **Tarea 1.1: Eliminar Memory Leak en Boost.cc**
|
||||
```cpp
|
||||
// ANTES (línea 51 en Boost.h):
|
||||
FeatureSelect* featureSelector = nullptr;
|
||||
|
||||
// DESPUÉS:
|
||||
std::unique_ptr<FeatureSelect> featureSelector;
|
||||
|
||||
// ANTES (líneas 124-141 en Boost.cc):
|
||||
if (select_features_algorithm == SelectFeatures.CFS) {
|
||||
featureSelector = new CFS(...);
|
||||
}
|
||||
// ...
|
||||
delete featureSelector;
|
||||
|
||||
// DESPUÉS:
|
||||
if (select_features_algorithm == SelectFeatures.CFS) {
|
||||
featureSelector = std::make_unique<CFS>(...);
|
||||
}
|
||||
// ... automática limpieza del smart pointer
|
||||
```
|
||||
|
||||
**Estimación:** 2 horas
|
||||
**Prioridad:** CRÍTICA
|
||||
|
||||
#### **Tarea 1.2: Optimizar BayesMetrics::SelectKPairs()**
|
||||
```cpp
|
||||
// SOLUCIÓN PROPUESTA:
|
||||
std::vector<std::pair<int, int>> Metrics::SelectKPairs(
|
||||
const torch::Tensor& weights,
|
||||
std::vector<int>& featuresExcluded,
|
||||
bool ascending, unsigned k) {
|
||||
|
||||
// ✅ O(1) lookups en lugar de O(n)
|
||||
std::unordered_set<int> excludedSet(featuresExcluded.begin(), featuresExcluded.end());
|
||||
|
||||
auto n = features.size();
|
||||
scoresKPairs.clear();
|
||||
scoresKPairs.reserve((n * (n-1)) / 2); // ✅ Reserve memoria
|
||||
|
||||
for (int i = 0; i < n - 1; ++i) {
|
||||
if (excludedSet.count(i)) continue; // ✅ O(1)
|
||||
for (int j = i + 1; j < n; ++j) {
|
||||
if (excludedSet.count(j)) continue; // ✅ O(1)
|
||||
// resto del procesamiento...
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ nth_element en lugar de sort completo
|
||||
if (k > 0 && k < scoresKPairs.size()) {
|
||||
std::nth_element(scoresKPairs.begin(),
|
||||
scoresKPairs.begin() + k,
|
||||
scoresKPairs.end());
|
||||
scoresKPairs.resize(k);
|
||||
}
|
||||
return pairsKBest;
|
||||
}
|
||||
```
|
||||
|
||||
**Beneficio:** 50x mejora de performance (de O(n³) a O(n² log k))
|
||||
**Estimación:** 4 horas
|
||||
**Prioridad:** CRÍTICA
|
||||
|
||||
#### **Tarea 1.3: Implementar Thread Pool**
|
||||
```cpp
|
||||
// SOLUCIÓN PROPUESTA para Network.cc:
|
||||
void Network::predict_tensor_optimized(const torch::Tensor& samples, const bool proba) {
|
||||
const int num_threads = std::min(
|
||||
static_cast<int>(std::thread::hardware_concurrency()),
|
||||
static_cast<int>(samples.size(1))
|
||||
);
|
||||
const int batch_size = (samples.size(1) + num_threads - 1) / num_threads;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(num_threads);
|
||||
|
||||
for (int t = 0; t < num_threads; ++t) {
|
||||
int start = t * batch_size;
|
||||
int end = std::min(start + batch_size, static_cast<int>(samples.size(1)));
|
||||
|
||||
threads.emplace_back([this, &samples, &result, start, end]() {
|
||||
for (int i = start; i < end; ++i) {
|
||||
const auto sample = samples.index({ "...", i });
|
||||
auto prediction = predict_sample(sample);
|
||||
// Thread-safe escritura
|
||||
std::lock_guard<std::mutex> lock(result_mutex);
|
||||
result.index_put_({ i, "..." }, torch::tensor(prediction));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Beneficio:** 4-8x mejora en predicción con múltiples cores
|
||||
**Estimación:** 6 horas
|
||||
**Prioridad:** CRÍTICA
|
||||
|
||||
### 3.2 Fase 2: Optimizaciones Importantes (Semanas 3-4)
|
||||
|
||||
#### **Tarea 2.1: Refactoring de Funciones Largas**
|
||||
|
||||
**XSP2DE.cc** - Dividir en funciones más pequeñas:
|
||||
```cpp
|
||||
// ANTES: Una función de 575 líneas
|
||||
void XSP2DE::buildModel(const torch::Tensor& weights) {
|
||||
// ... 575 líneas de código
|
||||
}
|
||||
|
||||
// DESPUÉS: Funciones especializadas
|
||||
class XSP2DE {
|
||||
private:
|
||||
void initializeHyperparameters();
|
||||
void selectFeatures(const torch::Tensor& weights);
|
||||
void buildSubModels();
|
||||
void trainIndividualModels(const torch::Tensor& weights);
|
||||
|
||||
public:
|
||||
void buildModel(const torch::Tensor& weights) override {
|
||||
initializeHyperparameters();
|
||||
selectFeatures(weights);
|
||||
buildSubModels();
|
||||
trainIndividualModels(weights);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Estimación:** 8 horas
|
||||
**Beneficio:** Mejora mantenibilidad y testing
|
||||
|
||||
#### **Tarea 2.2: Optimizar Union-Find en MST**
|
||||
```cpp
|
||||
// SOLUCIÓN PROPUESTA para Mst.cc:
|
||||
class UnionFind {
|
||||
private:
|
||||
std::vector<int> parent, rank;
|
||||
|
||||
public:
|
||||
UnionFind(int n) : parent(n), rank(n, 0) {
|
||||
std::iota(parent.begin(), parent.end(), 0);
|
||||
}
|
||||
|
||||
int find_set(int i) {
|
||||
if (i != parent[i])
|
||||
parent[i] = find_set(parent[i]); // ✅ Path compression
|
||||
return parent[i];
|
||||
}
|
||||
|
||||
bool union_set(int u, int v) {
|
||||
u = find_set(u);
|
||||
v = find_set(v);
|
||||
if (u == v) return false;
|
||||
|
||||
// ✅ Union by rank
|
||||
if (rank[u] < rank[v]) std::swap(u, v);
|
||||
parent[v] = u;
|
||||
if (rank[u] == rank[v]) rank[u]++;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Beneficio:** Mejora de O(V²) a O(E log V)
|
||||
**Estimación:** 4 horas
|
||||
|
||||
#### **Tarea 2.3: Eliminar Copias Innecesarias de Tensores**
|
||||
```cpp
|
||||
// ANTES (múltiples archivos):
|
||||
X = X.to(torch::kFloat32); // ❌ Copia completa
|
||||
y = y.to(torch::kFloat32); // ❌ Copia completa
|
||||
|
||||
// DESPUÉS:
|
||||
torch::Tensor X = samples.index({Slice(0, n_features), Slice()})
|
||||
.t()
|
||||
.to(torch::kFloat32); // ✅ Una sola conversión
|
||||
|
||||
torch::Tensor y = samples.index({-1, Slice()})
|
||||
.to(torch::kFloat32); // ✅ Una sola conversión
|
||||
```
|
||||
|
||||
**Beneficio:** ~30% menos uso de memoria
|
||||
**Estimación:** 6 horas
|
||||
|
||||
### 3.3 Fase 3: Mejoras de Robustez (Semanas 5-6)
|
||||
|
||||
#### **Tarea 3.1: Implementar Validación Comprehensiva**
|
||||
```cpp
|
||||
// TEMPLATE PARA VALIDACIÓN:
|
||||
template<typename T>
|
||||
void validateInput(const std::vector<T>& data, const std::string& name) {
|
||||
if (data.empty()) {
|
||||
throw std::invalid_argument(name + " cannot be empty");
|
||||
}
|
||||
}
|
||||
|
||||
void validateTensorDimensions(const torch::Tensor& tensor,
|
||||
const std::vector<int64_t>& expected_dims) {
|
||||
if (tensor.sizes() != expected_dims) {
|
||||
throw std::invalid_argument("Tensor dimensions mismatch");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **Tarea 3.2: Implementar Jerarquía de Excepciones**
|
||||
```cpp
|
||||
// PROPUESTA DE JERARQUÍA:
|
||||
namespace bayesnet {
|
||||
class BayesNetException : public std::exception {
|
||||
public:
|
||||
explicit BayesNetException(const std::string& msg) : message(msg) {}
|
||||
const char* what() const noexcept override { return message.c_str(); }
|
||||
private:
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class InvalidInputException : public BayesNetException {
|
||||
public:
|
||||
explicit InvalidInputException(const std::string& msg)
|
||||
: BayesNetException("Invalid input: " + msg) {}
|
||||
};
|
||||
|
||||
class ModelNotFittedException : public BayesNetException {
|
||||
public:
|
||||
ModelNotFittedException()
|
||||
: BayesNetException("Model has not been fitted") {}
|
||||
};
|
||||
|
||||
class DimensionMismatchException : public BayesNetException {
|
||||
public:
|
||||
explicit DimensionMismatchException(const std::string& msg)
|
||||
: BayesNetException("Dimension mismatch: " + msg) {}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### **Tarea 3.3: Mejorar Cobertura de Tests**
|
||||
```cpp
|
||||
// TESTS ADICIONALES NECESARIOS:
|
||||
TEST_CASE("Edge Cases", "[FeatureSelection]") {
|
||||
SECTION("Empty dataset") {
|
||||
torch::Tensor empty_dataset = torch::empty({0, 0});
|
||||
std::vector<std::string> empty_features;
|
||||
|
||||
REQUIRE_THROWS_AS(
|
||||
CFS(empty_dataset, empty_features, "class", 0, 2, torch::ones({1})),
|
||||
InvalidInputException
|
||||
);
|
||||
}
|
||||
|
||||
SECTION("Single feature") {
|
||||
// Test comportamiento con un solo feature
|
||||
}
|
||||
|
||||
SECTION("All features excluded") {
|
||||
// Test cuando todas las features están excluidas
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 Fase 4: Mejoras de Performance Avanzadas (Semanas 7-8)
|
||||
|
||||
#### **Tarea 4.1: Paralelización con OpenMP**
|
||||
```cpp
|
||||
// EXAMPLE PARA BUCLES CRÍTICOS:
|
||||
#include <omp.h>
|
||||
|
||||
void computeIntensiveOperation(const torch::Tensor& data) {
|
||||
const int n = data.size(0);
|
||||
std::vector<double> results(n);
|
||||
|
||||
#pragma omp parallel for
|
||||
for (int i = 0; i < n; ++i) {
|
||||
results[i] = expensiveComputation(data[i]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### **Tarea 4.2: Memory Pool para Operaciones Frecuentes**
|
||||
```cpp
|
||||
// PROPUESTA DE MEMORY POOL:
|
||||
class TensorPool {
|
||||
private:
|
||||
std::stack<torch::Tensor> available_tensors;
|
||||
std::mutex pool_mutex;
|
||||
|
||||
public:
|
||||
torch::Tensor acquire(const std::vector<int64_t>& shape) {
|
||||
std::lock_guard<std::mutex> lock(pool_mutex);
|
||||
if (!available_tensors.empty()) {
|
||||
auto tensor = available_tensors.top();
|
||||
available_tensors.pop();
|
||||
return tensor.resize_(shape);
|
||||
}
|
||||
return torch::zeros(shape);
|
||||
}
|
||||
|
||||
void release(torch::Tensor tensor) {
|
||||
std::lock_guard<std::mutex> lock(pool_mutex);
|
||||
available_tensors.push(tensor);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 4. Estimaciones y Timeline
|
||||
|
||||
### 4.1 Resumen de Esfuerzo
|
||||
| Fase | Tareas | Estimación | Beneficio |
|
||||
|------|--------|------------|-----------|
|
||||
| Fase 1 | Problemas Críticos | 12 horas | 10-50x mejora performance |
|
||||
| Fase 2 | Optimizaciones | 18 horas | Mantenibilidad + 30% menos memoria |
|
||||
| Fase 3 | Robustez | 16 horas | Estabilidad y debugging |
|
||||
| Fase 4 | Performance Avanzada | 12 horas | Escalabilidad |
|
||||
| **Total** | | **58 horas** | **Transformación significativa** |
|
||||
|
||||
### 4.2 Timeline Sugerido
|
||||
```
|
||||
Semana 1: [CRÍTICO] Memory leak + BayesMetrics
|
||||
Semana 2: [CRÍTICO] Thread pool + validación básica
|
||||
Semana 3: [IMPORTANTE] Refactoring XSP2DE + MST
|
||||
Semana 4: [IMPORTANTE] Optimización tensores + duplicación
|
||||
Semana 5: [ROBUSTEZ] Validación + excepciones
|
||||
Semana 6: [ROBUSTEZ] Tests adicionales + edge cases
|
||||
Semana 7: [AVANZADO] Paralelización OpenMP
|
||||
Semana 8: [AVANZADO] Memory pool + optimizaciones finales
|
||||
```
|
||||
|
||||
## 5. Impacto Esperado
|
||||
|
||||
### 5.1 Performance
|
||||
- **50x más rápido** en operaciones de feature selection
|
||||
- **4-8x más rápido** en predicción con datasets grandes
|
||||
- **30% menos uso de memoria** eliminando copias innecesarias
|
||||
- **Escalabilidad mejorada** con paralelización
|
||||
|
||||
### 5.2 Mantenibilidad
|
||||
- **Funciones más pequeñas** y especializadas
|
||||
- **Mejor separación de responsabilidades**
|
||||
- **Testing más comprehensivo**
|
||||
- **Debugging más fácil** con excepciones específicas
|
||||
|
||||
### 5.3 Robustez
|
||||
- **Eliminación de memory leaks**
|
||||
- **Validación comprehensiva de entrada**
|
||||
- **Manejo robusto de casos edge**
|
||||
- **Mejor reportes de error**
|
||||
|
||||
## 6. Recomendaciones Adicionales
|
||||
|
||||
### 6.1 Herramientas de Desarrollo
|
||||
- **Análisis estático:** Implementar clang-static-analyzer y cppcheck
|
||||
- **Sanitizers:** Usar AddressSanitizer y ThreadSanitizer en CI
|
||||
- **Profiling:** Integrar valgrind y perf para análisis de performance
|
||||
- **Benchmarking:** Implementar Google Benchmark para tests de regression
|
||||
|
||||
### 6.2 Proceso de Desarrollo
|
||||
- **Code reviews obligatorios** para cambios críticos
|
||||
- **CI/CD con tests automáticos** en múltiples plataformas
|
||||
- **Métricas de calidad** integradas (cobertura, complejidad ciclomática)
|
||||
- **Documentación de algoritmos** con complejidad y referencias
|
||||
|
||||
### 6.3 Monitoreo de Performance
|
||||
```cpp
|
||||
// PROPUESTA DE PROFILING INTEGRADO:
|
||||
class PerformanceProfiler {
|
||||
private:
|
||||
std::unordered_map<std::string, std::chrono::duration<double>> timings;
|
||||
|
||||
public:
|
||||
class ScopedTimer {
|
||||
// RAII timer para medir automáticamente
|
||||
};
|
||||
|
||||
void startProfiling(const std::string& operation);
|
||||
void endProfiling(const std::string& operation);
|
||||
void generateReport();
|
||||
};
|
||||
```
|
||||
|
||||
## 7. Conclusiones
|
||||
|
||||
BayesNet es una biblioteca sólida con una arquitectura bien diseñada y uso apropiado de técnicas modernas de C++. Sin embargo, existen oportunidades significativas de mejora que pueden transformar dramáticamente su performance y mantenibilidad.
|
||||
|
||||
### Prioridades Inmediatas:
|
||||
1. **Eliminar memory leak crítico** en Boost.cc
|
||||
2. **Optimizar algoritmo O(n³)** en BayesMetrics.cc
|
||||
3. **Implementar thread pool eficiente** en Network.cc
|
||||
|
||||
### Beneficios del Plan de Mejoras:
|
||||
- **Performance:** 10-50x mejora en operaciones críticas
|
||||
- **Memoria:** 30% reducción en uso de memoria
|
||||
- **Mantenibilidad:** Código más modular y testing comprehensivo
|
||||
- **Robustez:** Eliminación de crashes y mejor handling de errores
|
||||
|
||||
La implementación de estas mejoras convertirá BayesNet en una biblioteca de clase industrial, ready para production en entornos de alto rendimiento y misión crítica.
|
||||
|
||||
---
|
||||
|
||||
**Próximos Pasos Recomendados:**
|
||||
1. Revisar y aprobar este plan de mejoras
|
||||
2. Establecer prioridades basadas en necesidades del proyecto
|
||||
3. Implementar mejoras en el orden sugerido
|
||||
4. Establecer métricas de success para cada fase
|
||||
5. Configurar CI/CD para validar mejoras automáticamente
|
||||
@@ -1,13 +0,0 @@
|
||||
include_directories(
|
||||
${BayesNet_SOURCE_DIR}/lib/log
|
||||
${BayesNet_SOURCE_DIR}/lib/mdlp/src
|
||||
${BayesNet_SOURCE_DIR}/lib/folding
|
||||
${BayesNet_SOURCE_DIR}/lib/json/include
|
||||
${BayesNet_SOURCE_DIR}
|
||||
${CMAKE_BINARY_DIR}/configured_files/include
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE Sources "*.cc")
|
||||
|
||||
add_library(BayesNet ${Sources})
|
||||
target_link_libraries(BayesNet fimdlp "${TORCH_LIBRARIES}")
|
||||
@@ -10,17 +10,16 @@
|
||||
#include "Classifier.h"
|
||||
namespace bayesnet {
|
||||
class KDB : public Classifier {
|
||||
private:
|
||||
int k;
|
||||
float theta;
|
||||
protected:
|
||||
void add_m_edges(int idx, std::vector<int>& S, torch::Tensor& weights);
|
||||
void buildModel(const torch::Tensor& weights) override;
|
||||
public:
|
||||
explicit KDB(int k, float theta = 0.03);
|
||||
virtual ~KDB() = default;
|
||||
void setHyperparameters(const nlohmann::json& hyperparameters_) override;
|
||||
std::vector<std::string> graph(const std::string& name = "KDB") const override;
|
||||
protected:
|
||||
int k;
|
||||
float theta;
|
||||
void add_m_edges(int idx, std::vector<int>& S, torch::Tensor& weights);
|
||||
void buildModel(const torch::Tensor& weights) override;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -7,7 +7,25 @@
|
||||
#include "KDBLd.h"
|
||||
|
||||
namespace bayesnet {
|
||||
KDBLd::KDBLd(int k) : KDB(k), Proposal(dataset, features, className) {}
|
||||
KDBLd::KDBLd(int k) : KDB(k), Proposal(dataset, features, className)
|
||||
{
|
||||
validHyperparameters = validHyperparameters_ld;
|
||||
validHyperparameters.push_back("k");
|
||||
validHyperparameters.push_back("theta");
|
||||
}
|
||||
void KDBLd::setHyperparameters(const nlohmann::json& hyperparameters_)
|
||||
{
|
||||
auto hyperparameters = hyperparameters_;
|
||||
if (hyperparameters.contains("k")) {
|
||||
k = hyperparameters["k"];
|
||||
hyperparameters.erase("k");
|
||||
}
|
||||
if (hyperparameters.contains("theta")) {
|
||||
theta = hyperparameters["theta"];
|
||||
hyperparameters.erase("theta");
|
||||
}
|
||||
Proposal::setHyperparameters(hyperparameters);
|
||||
}
|
||||
KDBLd& KDBLd::fit(torch::Tensor& X_, torch::Tensor& y_, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_, const Smoothing_t smoothing)
|
||||
{
|
||||
checkInput(X_, y_);
|
||||
@@ -28,6 +46,11 @@ namespace bayesnet {
|
||||
auto Xt = prepareX(X);
|
||||
return KDB::predict(Xt);
|
||||
}
|
||||
torch::Tensor KDBLd::predict_proba(torch::Tensor& X)
|
||||
{
|
||||
auto Xt = prepareX(X);
|
||||
return KDB::predict_proba(Xt);
|
||||
}
|
||||
std::vector<std::string> KDBLd::graph(const std::string& name) const
|
||||
{
|
||||
return KDB::graph(name);
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
|
||||
namespace bayesnet {
|
||||
class KDBLd : public KDB, public Proposal {
|
||||
private:
|
||||
public:
|
||||
explicit KDBLd(int k);
|
||||
virtual ~KDBLd() = default;
|
||||
KDBLd& fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, map<std::string, std::vector<int>>& states, const Smoothing_t smoothing) override;
|
||||
std::vector<std::string> graph(const std::string& name = "KDB") const override;
|
||||
void setHyperparameters(const nlohmann::json& hyperparameters_) override;
|
||||
torch::Tensor predict(torch::Tensor& X) override;
|
||||
torch::Tensor predict_proba(torch::Tensor& X) override;
|
||||
static inline std::string version() { return "0.0.1"; };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,13 +7,42 @@
|
||||
#include "Proposal.h"
|
||||
|
||||
namespace bayesnet {
|
||||
Proposal::Proposal(torch::Tensor& dataset_, std::vector<std::string>& features_, std::string& className_) : pDataset(dataset_), pFeatures(features_), pClassName(className_) {}
|
||||
Proposal::~Proposal()
|
||||
Proposal::Proposal(torch::Tensor& dataset_, std::vector<std::string>& features_, std::string& className_) : pDataset(dataset_), pFeatures(features_), pClassName(className_)
|
||||
{
|
||||
for (auto& [key, value] : discretizers) {
|
||||
delete value;
|
||||
}
|
||||
void Proposal::setHyperparameters(const nlohmann::json& hyperparameters_)
|
||||
{
|
||||
auto hyperparameters = hyperparameters_;
|
||||
if (hyperparameters.contains("ld_proposed_cuts")) {
|
||||
ld_params.proposed_cuts = hyperparameters["ld_proposed_cuts"];
|
||||
hyperparameters.erase("ld_proposed_cuts");
|
||||
}
|
||||
if (hyperparameters.contains("mdlp_max_depth")) {
|
||||
ld_params.max_depth = hyperparameters["mdlp_max_depth"];
|
||||
hyperparameters.erase("mdlp_max_depth");
|
||||
}
|
||||
if (hyperparameters.contains("mdlp_min_length")) {
|
||||
ld_params.min_length = hyperparameters["mdlp_min_length"];
|
||||
hyperparameters.erase("mdlp_min_length");
|
||||
}
|
||||
if (hyperparameters.contains("ld_algorithm")) {
|
||||
auto algorithm = hyperparameters["ld_algorithm"];
|
||||
hyperparameters.erase("ld_algorithm");
|
||||
if (algorithm == "MDLP") {
|
||||
discretizationType = discretization_t::MDLP;
|
||||
} else if (algorithm == "BINQ") {
|
||||
discretizationType = discretization_t::BINQ;
|
||||
} else if (algorithm == "BINU") {
|
||||
discretizationType = discretization_t::BINU;
|
||||
} else {
|
||||
throw std::invalid_argument("Invalid discretization algorithm: " + algorithm.get<std::string>());
|
||||
}
|
||||
}
|
||||
if (!hyperparameters.empty()) {
|
||||
throw std::invalid_argument("Invalid hyperparameters for Proposal: " + hyperparameters.dump());
|
||||
}
|
||||
}
|
||||
|
||||
void Proposal::checkInput(const torch::Tensor& X, const torch::Tensor& y)
|
||||
{
|
||||
if (!torch::is_floating_point(X)) {
|
||||
@@ -23,6 +52,7 @@ namespace bayesnet {
|
||||
throw std::invalid_argument("y must be an integer tensor");
|
||||
}
|
||||
}
|
||||
// Fit method for single classifier
|
||||
map<std::string, std::vector<int>> Proposal::localDiscretizationProposal(const map<std::string, std::vector<int>>& oldStates, Network& model)
|
||||
{
|
||||
// order of local discretization is important. no good 0, 1, 2...
|
||||
@@ -83,8 +113,15 @@ namespace bayesnet {
|
||||
pDataset = torch::zeros({ n + 1, m }, torch::kInt32);
|
||||
auto yv = std::vector<int>(y.data_ptr<int>(), y.data_ptr<int>() + y.size(0));
|
||||
// discretize input data by feature(row)
|
||||
std::unique_ptr<mdlp::Discretizer> discretizer;
|
||||
for (auto i = 0; i < pFeatures.size(); ++i) {
|
||||
auto* discretizer = new mdlp::CPPFImdlp();
|
||||
if (discretizationType == discretization_t::BINQ) {
|
||||
discretizer = std::make_unique<mdlp::BinDisc>(ld_params.proposed_cuts, mdlp::strategy_t::QUANTILE);
|
||||
} else if (discretizationType == discretization_t::BINU) {
|
||||
discretizer = std::make_unique<mdlp::BinDisc>(ld_params.proposed_cuts, mdlp::strategy_t::UNIFORM);
|
||||
} else { // Default is MDLP
|
||||
discretizer = std::make_unique<mdlp::CPPFImdlp>(ld_params.min_length, ld_params.max_depth, ld_params.proposed_cuts);
|
||||
}
|
||||
auto Xt_ptr = Xf.index({ i }).data_ptr<float>();
|
||||
auto Xt = std::vector<float>(Xt_ptr, Xt_ptr + Xf.size(1));
|
||||
discretizer->fit(Xt, yv);
|
||||
@@ -92,7 +129,7 @@ namespace bayesnet {
|
||||
auto xStates = std::vector<int>(discretizer->getCutPoints().size() + 1);
|
||||
iota(xStates.begin(), xStates.end(), 0);
|
||||
states[pFeatures[i]] = xStates;
|
||||
discretizers[pFeatures[i]] = discretizer;
|
||||
discretizers[pFeatures[i]] = std::move(discretizer);
|
||||
}
|
||||
int n_classes = torch::max(y).item<int>() + 1;
|
||||
auto yStates = std::vector<int>(n_classes);
|
||||
|
||||
@@ -9,15 +9,17 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <torch/torch.h>
|
||||
#include <CPPFImdlp.h>
|
||||
#include <fimdlp/CPPFImdlp.h>
|
||||
#include <fimdlp/BinDisc.h>
|
||||
#include "bayesnet/network/Network.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "Classifier.h"
|
||||
|
||||
namespace bayesnet {
|
||||
class Proposal {
|
||||
public:
|
||||
Proposal(torch::Tensor& pDataset, std::vector<std::string>& features_, std::string& className_);
|
||||
virtual ~Proposal();
|
||||
void setHyperparameters(const nlohmann::json& hyperparameters_);
|
||||
protected:
|
||||
void checkInput(const torch::Tensor& X, const torch::Tensor& y);
|
||||
torch::Tensor prepareX(torch::Tensor& X);
|
||||
@@ -25,12 +27,24 @@ namespace bayesnet {
|
||||
map<std::string, std::vector<int>> fit_local_discretization(const torch::Tensor& y);
|
||||
torch::Tensor Xf; // X continuous nxm tensor
|
||||
torch::Tensor y; // y discrete nx1 tensor
|
||||
map<std::string, mdlp::CPPFImdlp*> discretizers;
|
||||
map<std::string, std::unique_ptr<mdlp::Discretizer>> discretizers;
|
||||
// MDLP parameters
|
||||
struct {
|
||||
size_t min_length = 3; // Minimum length of the interval to consider it in mdlp
|
||||
float proposed_cuts = 0.0; // Proposed cuts for the Discretization algorithm
|
||||
int max_depth = std::numeric_limits<int>::max(); // Maximum depth of the MDLP tree
|
||||
} ld_params;
|
||||
nlohmann::json validHyperparameters_ld = { "ld_algorithm", "ld_proposed_cuts", "mdlp_min_length", "mdlp_max_depth" };
|
||||
private:
|
||||
std::vector<int> factorize(const std::vector<std::string>& labels_t);
|
||||
torch::Tensor& pDataset; // (n+1)xm tensor
|
||||
std::vector<std::string>& pFeatures;
|
||||
std::string& pClassName;
|
||||
enum class discretization_t {
|
||||
MDLP,
|
||||
BINQ,
|
||||
BINU
|
||||
} discretizationType = discretization_t::MDLP; // Default discretization type
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
#include "SPODELd.h"
|
||||
|
||||
namespace bayesnet {
|
||||
SPODELd::SPODELd(int root) : SPODE(root), Proposal(dataset, features, className) {}
|
||||
SPODELd::SPODELd(int root) : SPODE(root), Proposal(dataset, features, className)
|
||||
{
|
||||
validHyperparameters = validHyperparameters_ld; // Inherits the valid hyperparameters from Proposal
|
||||
}
|
||||
|
||||
SPODELd& SPODELd::fit(torch::Tensor& X_, torch::Tensor& y_, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_, const Smoothing_t smoothing)
|
||||
{
|
||||
checkInput(X_, y_);
|
||||
@@ -43,6 +47,11 @@ namespace bayesnet {
|
||||
auto Xt = prepareX(X);
|
||||
return SPODE::predict(Xt);
|
||||
}
|
||||
torch::Tensor SPODELd::predict_proba(torch::Tensor& X)
|
||||
{
|
||||
auto Xt = prepareX(X);
|
||||
return SPODE::predict_proba(Xt);
|
||||
}
|
||||
std::vector<std::string> SPODELd::graph(const std::string& name) const
|
||||
{
|
||||
return SPODE::graph(name);
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace bayesnet {
|
||||
SPODELd& commonFit(const std::vector<std::string>& features, const std::string& className, map<std::string, std::vector<int>>& states, const Smoothing_t smoothing);
|
||||
std::vector<std::string> graph(const std::string& name = "SPODELd") const override;
|
||||
torch::Tensor predict(torch::Tensor& X) override;
|
||||
torch::Tensor predict_proba(torch::Tensor& X) override;
|
||||
static inline std::string version() { return "0.0.1"; };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ namespace bayesnet {
|
||||
auto Xt = prepareX(X);
|
||||
return TAN::predict(Xt);
|
||||
}
|
||||
torch::Tensor TANLd::predict_proba(torch::Tensor& X)
|
||||
{
|
||||
auto Xt = prepareX(X);
|
||||
return TAN::predict_proba(Xt);
|
||||
}
|
||||
std::vector<std::string> TANLd::graph(const std::string& name) const
|
||||
{
|
||||
return TAN::graph(name);
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace bayesnet {
|
||||
TANLd& fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, map<std::string, std::vector<int>>& states, const Smoothing_t smoothing) override;
|
||||
std::vector<std::string> graph(const std::string& name = "TANLd") const override;
|
||||
torch::Tensor predict(torch::Tensor& X) override;
|
||||
torch::Tensor predict_proba(torch::Tensor& X) override;
|
||||
};
|
||||
}
|
||||
#endif // !TANLD_H
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace bayesnet {
|
||||
AODELd::AODELd(bool predict_voting) : Ensemble(predict_voting), Proposal(dataset, features, className)
|
||||
{
|
||||
validHyperparameters = validHyperparameters_ld; // Inherits the valid hyperparameters from Proposal
|
||||
}
|
||||
AODELd& AODELd::fit(torch::Tensor& X_, torch::Tensor& y_, const std::vector<std::string>& features_, const std::string& className_, map<std::string, std::vector<int>>& states_, const Smoothing_t smoothing)
|
||||
{
|
||||
@@ -31,6 +32,7 @@ namespace bayesnet {
|
||||
models.clear();
|
||||
for (int i = 0; i < features.size(); ++i) {
|
||||
models.push_back(std::make_unique<SPODELd>(i));
|
||||
models.back()->setHyperparameters(hyperparameters);
|
||||
}
|
||||
n_models = models.size();
|
||||
significanceModels = std::vector<double>(n_models, 1.0);
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace bayesnet {
|
||||
protected:
|
||||
void trainModel(const torch::Tensor& weights, const Smoothing_t smoothing) override;
|
||||
void buildModel(const torch::Tensor& weights) override;
|
||||
private:
|
||||
nlohmann::json hyperparameters = {}; // Hyperparameters for the model
|
||||
};
|
||||
}
|
||||
#endif // !AODELD_H
|
||||
@@ -4,14 +4,14 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// ***************************************************************
|
||||
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <limits.h>
|
||||
#include <tuple>
|
||||
#include "BoostAODE.h"
|
||||
#include "bayesnet/classifiers/SPODE.h"
|
||||
#include <loguru.hpp>
|
||||
#include <loguru.cpp>
|
||||
#include <limits.h>
|
||||
// #include <loguru.cpp>
|
||||
// #include <loguru.hpp>
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
|
||||
namespace bayesnet {
|
||||
|
||||
@@ -81,10 +81,8 @@ namespace bayesnet {
|
||||
std::shuffle(featureSelection.begin(), featureSelection.end(), g);
|
||||
}
|
||||
// Remove used features
|
||||
featureSelection.erase(remove_if(begin(featureSelection), end(featureSelection), [&](auto x)
|
||||
{ return std::find(begin(featuresUsed), end(featuresUsed), x) != end(featuresUsed);}),
|
||||
end(featureSelection)
|
||||
);
|
||||
featureSelection.erase(remove_if(begin(featureSelection), end(featureSelection), [&](auto x) { return std::find(begin(featuresUsed), end(featuresUsed), x) != end(featuresUsed); }),
|
||||
end(featureSelection));
|
||||
int k = bisection ? pow(2, tolerance) : 1;
|
||||
int counter = 0; // The model counter of the current pack
|
||||
// VLOG_SCOPE_F(1, "counter=%d k=%d featureSelection.size: %zu", counter, k, featureSelection.size());
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
#include <random>
|
||||
#include <tuple>
|
||||
|
||||
namespace bayesnet {
|
||||
XBAODE::XBAODE() : Boost(false) {
|
||||
validHyperparameters = {"alpha_block", "order", "convergence", "convergence_best", "bisection",
|
||||
"threshold", "maxTolerance", "predict_voting", "select_features"};
|
||||
}
|
||||
std::vector<int> XBAODE::initializeModels(const Smoothing_t smoothing) {
|
||||
namespace bayesnet
|
||||
{
|
||||
XBAODE::XBAODE() : Boost(false) {}
|
||||
std::vector<int> XBAODE::initializeModels(const Smoothing_t smoothing)
|
||||
{
|
||||
torch::Tensor weights_ = torch::full({m}, 1.0 / m, torch::kFloat64);
|
||||
std::vector<int> featuresSelected = featureSelection(weights_);
|
||||
for (const int &feature : featuresSelected) {
|
||||
@@ -26,8 +25,9 @@ std::vector<int> XBAODE::initializeModels(const Smoothing_t smoothing) {
|
||||
notes.push_back("Used features in initialization: " + std::to_string(featuresSelected.size()) + " of " +
|
||||
std::to_string(features.size()) + " with " + select_features_algorithm);
|
||||
return featuresSelected;
|
||||
}
|
||||
void XBAODE::trainModel(const torch::Tensor &weights, const bayesnet::Smoothing_t smoothing) {
|
||||
}
|
||||
void XBAODE::trainModel(const torch::Tensor &weights, const bayesnet::Smoothing_t smoothing)
|
||||
{
|
||||
X_train_ = TensorUtils::to_matrix(X_train);
|
||||
y_train_ = TensorUtils::to_vector<int>(y_train);
|
||||
if (convergence) {
|
||||
@@ -180,5 +180,5 @@ void XBAODE::trainModel(const torch::Tensor &weights, const bayesnet::Smoothing_
|
||||
}
|
||||
notes.push_back("Number of models: " + std::to_string(n_models));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace bayesnet
|
||||
|
||||
@@ -4,81 +4,136 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// ***************************************************************
|
||||
|
||||
#include <limits>
|
||||
#include "bayesnet/utils/bayesnetUtils.h"
|
||||
#include "FeatureSelect.h"
|
||||
namespace bayesnet {
|
||||
FeatureSelect::FeatureSelect(const torch::Tensor& samples, const std::vector<std::string>& features, const std::string& className, const int maxFeatures, const int classNumStates, const torch::Tensor& weights) :
|
||||
Metrics(samples, features, className, classNumStates), maxFeatures(maxFeatures == 0 ? samples.size(0) - 1 : maxFeatures), weights(weights)
|
||||
|
||||
namespace bayesnet {
|
||||
|
||||
using namespace torch::indexing; // for Ellipsis constant
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// ctor
|
||||
//---------------------------------------------------------------------
|
||||
FeatureSelect::FeatureSelect(const torch::Tensor& samples,
|
||||
const std::vector<std::string>& features,
|
||||
const std::string& className,
|
||||
int maxFeatures,
|
||||
int classNumStates,
|
||||
const torch::Tensor& weights)
|
||||
: Metrics(samples, features, className, classNumStates),
|
||||
maxFeatures(maxFeatures == 0 ? samples.size(0) - 1 : maxFeatures),
|
||||
weights(weights)
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// public helpers
|
||||
//---------------------------------------------------------------------
|
||||
void FeatureSelect::initialize()
|
||||
{
|
||||
selectedFeatures.clear();
|
||||
selectedScores.clear();
|
||||
suLabels.clear();
|
||||
suFeatures.clear();
|
||||
|
||||
fitted = false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Symmetrical Uncertainty (SU)
|
||||
//---------------------------------------------------------------------
|
||||
double FeatureSelect::symmetricalUncertainty(int a, int b)
|
||||
{
|
||||
/*
|
||||
Compute symmetrical uncertainty. Normalize* information gain (mutual
|
||||
information) with the entropies of the features in order to compensate
|
||||
the bias due to high cardinality features. *Range [0, 1]
|
||||
(https://www.sciencedirect.com/science/article/pii/S0020025519303603)
|
||||
* Compute symmetrical uncertainty. Normalises the information gain
|
||||
* (mutual information) with the entropies of the variables to compensate
|
||||
* the bias due to high‑cardinality features. Range: [0, 1]
|
||||
* See: https://www.sciencedirect.com/science/article/pii/S0020025519303603
|
||||
*/
|
||||
auto x = samples.index({ a, "..." });
|
||||
auto y = samples.index({ b, "..." });
|
||||
auto mu = mutualInformation(x, y, weights);
|
||||
auto hx = entropy(x, weights);
|
||||
auto hy = entropy(y, weights);
|
||||
return 2.0 * mu / (hx + hy);
|
||||
|
||||
auto x = samples.index({ a, Ellipsis }); // row a => feature a
|
||||
auto y = (b >= 0) ? samples.index({ b, Ellipsis }) // row b (>=0) => feature b
|
||||
: samples.index({ -1, Ellipsis }); // ‑1 treated as last row = labels
|
||||
|
||||
double mu = mutualInformation(x, y, weights);
|
||||
double hx = entropy(x, weights);
|
||||
double hy = entropy(y, weights);
|
||||
|
||||
const double denom = hx + hy;
|
||||
if (denom == 0.0) return 0.0; // perfectly pure variables
|
||||
|
||||
return 2.0 * mu / denom;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// SU feature–class
|
||||
//---------------------------------------------------------------------
|
||||
void FeatureSelect::computeSuLabels()
|
||||
{
|
||||
// Compute Simmetrical Uncertainty between features and labels
|
||||
// Compute Symmetrical Uncertainty between each feature and the class labels
|
||||
// https://en.wikipedia.org/wiki/Symmetric_uncertainty
|
||||
for (int i = 0; i < features.size(); ++i) {
|
||||
suLabels.push_back(symmetricalUncertainty(i, -1));
|
||||
const int classIdx = static_cast<int>(samples.size(0)) - 1; // labels in last row
|
||||
suLabels.reserve(features.size());
|
||||
for (int i = 0; i < static_cast<int>(features.size()); ++i) {
|
||||
suLabels.emplace_back(symmetricalUncertainty(i, classIdx));
|
||||
}
|
||||
}
|
||||
double FeatureSelect::computeSuFeatures(const int firstFeature, const int secondFeature)
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// SU feature–feature with cache
|
||||
//---------------------------------------------------------------------
|
||||
double FeatureSelect::computeSuFeatures(int firstFeature, int secondFeature)
|
||||
{
|
||||
// Compute Simmetrical Uncertainty between features
|
||||
// https://en.wikipedia.org/wiki/Symmetric_uncertainty
|
||||
try {
|
||||
return suFeatures.at({ firstFeature, secondFeature });
|
||||
}
|
||||
catch (const std::out_of_range& e) {
|
||||
double result = symmetricalUncertainty(firstFeature, secondFeature);
|
||||
suFeatures[{firstFeature, secondFeature}] = result;
|
||||
// Order the pair to exploit symmetry => only one entry in the map
|
||||
auto ordered = std::minmax(firstFeature, secondFeature);
|
||||
const std::pair<int, int> key{ ordered.first, ordered.second };
|
||||
|
||||
auto it = suFeatures.find(key);
|
||||
if (it != suFeatures.end()) return it->second;
|
||||
|
||||
double result = symmetricalUncertainty(key.first, key.second);
|
||||
suFeatures[key] = result; // store once (symmetry handled by ordering)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Correlation‑based Feature Selection (CFS) merit
|
||||
//---------------------------------------------------------------------
|
||||
double FeatureSelect::computeMeritCFS()
|
||||
{
|
||||
double rcf = 0;
|
||||
for (auto feature : selectedFeatures) {
|
||||
rcf += suLabels[feature];
|
||||
}
|
||||
double rff = 0;
|
||||
int n = selectedFeatures.size();
|
||||
for (const auto& item : doCombinations(selectedFeatures)) {
|
||||
rff += computeSuFeatures(item.first, item.second);
|
||||
}
|
||||
return rcf / sqrt(n + (n * n - n) * rff);
|
||||
const int n = static_cast<int>(selectedFeatures.size());
|
||||
if (n == 0) return 0.0;
|
||||
|
||||
// average r_cf (feature–class)
|
||||
double rcf_sum = 0.0;
|
||||
for (int f : selectedFeatures) rcf_sum += suLabels[f];
|
||||
const double rcf_avg = rcf_sum / n;
|
||||
|
||||
// average r_ff (feature–feature)
|
||||
double rff_sum = 0.0;
|
||||
const auto& pairs = doCombinations(selectedFeatures); // generates each unordered pair once
|
||||
for (const auto& p : pairs) rff_sum += computeSuFeatures(p.first, p.second);
|
||||
|
||||
const double numPairs = n * (n - 1) * 0.5;
|
||||
const double rff_avg = (numPairs > 0) ? rff_sum / numPairs : 0.0;
|
||||
|
||||
// Merit_S = k * r_cf / sqrt( k + k*(k‑1) * r_ff ) (Hall, 1999)
|
||||
const double k = static_cast<double>(n);
|
||||
return (k * rcf_avg) / std::sqrt(k + k * (k - 1) * rff_avg);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// getters
|
||||
//---------------------------------------------------------------------
|
||||
std::vector<int> FeatureSelect::getFeatures() const
|
||||
{
|
||||
if (!fitted) {
|
||||
throw std::runtime_error("FeatureSelect not fitted");
|
||||
}
|
||||
if (!fitted) throw std::runtime_error("FeatureSelect not fitted");
|
||||
return selectedFeatures;
|
||||
}
|
||||
|
||||
std::vector<double> FeatureSelect::getScores() const
|
||||
{
|
||||
if (!fitted) {
|
||||
throw std::runtime_error("FeatureSelect not fitted");
|
||||
}
|
||||
if (!fitted) throw std::runtime_error("FeatureSelect not fitted");
|
||||
return selectedScores;
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,26 @@ namespace bayesnet {
|
||||
auto first_feature = pop_first(featureOrderCopy);
|
||||
selectedFeatures.push_back(first_feature);
|
||||
selectedScores.push_back(suLabels.at(first_feature));
|
||||
// Second with the score of the candidates
|
||||
selectedFeatures.push_back(pop_first(featureOrderCopy));
|
||||
auto merit = computeMeritCFS();
|
||||
selectedScores.push_back(merit);
|
||||
// Select second feature that maximizes merit with first
|
||||
double maxMerit = 0.0;
|
||||
int secondFeature = -1;
|
||||
for (const auto& candidate : featureOrderCopy) {
|
||||
selectedFeatures.push_back(candidate);
|
||||
double candidateMerit = computeMeritCFS();
|
||||
if (candidateMerit > maxMerit) {
|
||||
maxMerit = candidateMerit;
|
||||
secondFeature = candidate;
|
||||
}
|
||||
selectedFeatures.pop_back();
|
||||
}
|
||||
|
||||
if (secondFeature != -1) {
|
||||
selectedFeatures.push_back(secondFeature);
|
||||
selectedScores.push_back(maxMerit);
|
||||
// Remove from featureOrderCopy
|
||||
featureOrderCopy.erase(std::remove(featureOrderCopy.begin(), featureOrderCopy.end(), secondFeature), featureOrderCopy.end());
|
||||
}
|
||||
double merit = maxMerit;
|
||||
for (const auto feature : featureOrderCopy) {
|
||||
selectedFeatures.push_back(feature);
|
||||
// Compute merit with selectedFeatures
|
||||
|
||||
279
bayesnet/feature_selection/L1FS.cc
Normal file
279
bayesnet/feature_selection/L1FS.cc
Normal file
@@ -0,0 +1,279 @@
|
||||
// ***************************************************************
|
||||
// SPDX-FileCopyrightText: Copyright 2024 Ricardo Montañana Gómez
|
||||
// SPDX-FileType: SOURCE
|
||||
// SPDX-License-Identifier: MIT
|
||||
// ***************************************************************
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include "bayesnet/utils/bayesnetUtils.h"
|
||||
#include "L1FS.h"
|
||||
|
||||
namespace bayesnet {
|
||||
using namespace torch::indexing;
|
||||
|
||||
L1FS::L1FS(const torch::Tensor& samples,
|
||||
const std::vector<std::string>& features,
|
||||
const std::string& className,
|
||||
const int maxFeatures,
|
||||
const int classNumStates,
|
||||
const torch::Tensor& weights,
|
||||
const double alpha,
|
||||
const int maxIter,
|
||||
const double tolerance,
|
||||
const bool fitIntercept)
|
||||
: FeatureSelect(samples, features, className, maxFeatures, classNumStates, weights),
|
||||
alpha(alpha), maxIter(maxIter), tolerance(tolerance), fitIntercept(fitIntercept)
|
||||
{
|
||||
if (alpha < 0) {
|
||||
throw std::invalid_argument("Alpha (regularization strength) must be non-negative");
|
||||
}
|
||||
if (maxIter < 1) {
|
||||
throw std::invalid_argument("Maximum iterations must be positive");
|
||||
}
|
||||
if (tolerance <= 0) {
|
||||
throw std::invalid_argument("Tolerance must be positive");
|
||||
}
|
||||
|
||||
// Determine if this is a regression or classification task
|
||||
// For simplicity, assume binary classification if classNumStates == 2
|
||||
// and regression otherwise (this can be refined based on your needs)
|
||||
isRegression = (classNumStates > 2 || classNumStates == 0);
|
||||
}
|
||||
|
||||
void L1FS::fit()
|
||||
{
|
||||
initialize();
|
||||
|
||||
// Prepare data
|
||||
int n_samples = samples.size(1);
|
||||
int n_features = features.size();
|
||||
|
||||
// Extract features (all rows except last)
|
||||
auto X = samples.index({ Slice(0, n_features), Slice() }).t().contiguous();
|
||||
|
||||
// Extract labels (last row)
|
||||
auto y = samples.index({ -1, Slice() }).contiguous();
|
||||
|
||||
// Convert to float for numerical operations
|
||||
X = X.to(torch::kFloat32);
|
||||
y = y.to(torch::kFloat32);
|
||||
|
||||
// Normalize features for better convergence
|
||||
auto X_mean = X.mean(0);
|
||||
auto X_std = X.std(0);
|
||||
X_std = torch::where(X_std == 0, torch::ones_like(X_std), X_std);
|
||||
X = (X - X_mean) / X_std;
|
||||
|
||||
if (isRegression) {
|
||||
// Normalize y for regression
|
||||
auto y_mean = y.mean();
|
||||
auto y_std = y.std();
|
||||
if (y_std.item<double>() > 0) {
|
||||
y = (y - y_mean) / y_std;
|
||||
}
|
||||
fitLasso(X, y, weights);
|
||||
} else {
|
||||
// For binary classification
|
||||
fitL1Logistic(X, y, weights);
|
||||
}
|
||||
|
||||
// Select features based on non-zero coefficients
|
||||
std::vector<std::pair<int, double>> featureImportance;
|
||||
for (int i = 0; i < n_features; ++i) {
|
||||
double coef_magnitude = std::abs(coefficients[i]);
|
||||
if (coef_magnitude > 1e-10) { // Threshold for numerical zero
|
||||
featureImportance.push_back({ i, coef_magnitude });
|
||||
}
|
||||
}
|
||||
|
||||
// If all coefficients are zero (high regularization), select based on original feature-class correlation
|
||||
if (featureImportance.empty() && maxFeatures > 0) {
|
||||
// Compute SU with labels as fallback
|
||||
computeSuLabels();
|
||||
auto featureOrder = argsort(suLabels);
|
||||
|
||||
// Select top features by SU score
|
||||
int numToSelect = std::min(static_cast<int>(featureOrder.size()),
|
||||
std::min(maxFeatures, 3)); // At most 3 features as fallback
|
||||
|
||||
for (int i = 0; i < numToSelect; ++i) {
|
||||
selectedFeatures.push_back(featureOrder[i]);
|
||||
selectedScores.push_back(suLabels[featureOrder[i]]);
|
||||
}
|
||||
} else {
|
||||
// Sort by importance (absolute coefficient value)
|
||||
std::sort(featureImportance.begin(), featureImportance.end(),
|
||||
[](const auto& a, const auto& b) { return a.second > b.second; });
|
||||
|
||||
// Select top features up to maxFeatures
|
||||
int numToSelect = std::min(static_cast<int>(featureImportance.size()),
|
||||
maxFeatures);
|
||||
|
||||
for (int i = 0; i < numToSelect; ++i) {
|
||||
selectedFeatures.push_back(featureImportance[i].first);
|
||||
selectedScores.push_back(featureImportance[i].second);
|
||||
}
|
||||
}
|
||||
|
||||
fitted = true;
|
||||
}
|
||||
|
||||
void L1FS::fitLasso(const torch::Tensor& X, const torch::Tensor& y,
|
||||
const torch::Tensor& sampleWeights)
|
||||
{
|
||||
int n_samples = X.size(0);
|
||||
int n_features = X.size(1);
|
||||
|
||||
// Initialize coefficients
|
||||
coefficients.resize(n_features, 0.0);
|
||||
double intercept = 0.0;
|
||||
|
||||
// Ensure consistent types
|
||||
torch::Tensor weights = sampleWeights.to(torch::kFloat32);
|
||||
|
||||
// Coordinate descent for Lasso
|
||||
torch::Tensor residuals = y.clone();
|
||||
if (fitIntercept) {
|
||||
intercept = (y * weights).sum().item<float>() / weights.sum().item<float>();
|
||||
residuals = y - intercept;
|
||||
}
|
||||
|
||||
// Precompute feature norms
|
||||
std::vector<double> featureNorms(n_features);
|
||||
for (int j = 0; j < n_features; ++j) {
|
||||
auto Xj = X.index({ Slice(), j });
|
||||
featureNorms[j] = (Xj * Xj * weights).sum().item<float>();
|
||||
}
|
||||
|
||||
// Coordinate descent iterations
|
||||
for (int iter = 0; iter < maxIter; ++iter) {
|
||||
double maxChange = 0.0;
|
||||
|
||||
// Update each coordinate
|
||||
for (int j = 0; j < n_features; ++j) {
|
||||
auto Xj = X.index({ Slice(), j });
|
||||
|
||||
// Compute partial residuals (excluding feature j)
|
||||
torch::Tensor partialResiduals = residuals + coefficients[j] * Xj;
|
||||
|
||||
// Compute rho (correlation with residuals)
|
||||
double rho = (Xj * partialResiduals * weights).sum().item<float>();
|
||||
|
||||
// Soft thresholding
|
||||
double oldCoef = coefficients[j];
|
||||
coefficients[j] = softThreshold(rho, alpha) / featureNorms[j];
|
||||
|
||||
// Update residuals
|
||||
residuals = partialResiduals - coefficients[j] * Xj;
|
||||
|
||||
maxChange = std::max(maxChange, std::abs(coefficients[j] - oldCoef));
|
||||
}
|
||||
|
||||
// Update intercept if needed
|
||||
if (fitIntercept) {
|
||||
double oldIntercept = intercept;
|
||||
intercept = (residuals * weights).sum().item<float>() /
|
||||
weights.sum().item<float>();
|
||||
residuals = residuals - (intercept - oldIntercept);
|
||||
maxChange = std::max(maxChange, std::abs(intercept - oldIntercept));
|
||||
}
|
||||
|
||||
// Check convergence
|
||||
if (maxChange < tolerance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void L1FS::fitL1Logistic(const torch::Tensor& X, const torch::Tensor& y,
|
||||
const torch::Tensor& sampleWeights)
|
||||
{
|
||||
int n_samples = X.size(0);
|
||||
int n_features = X.size(1);
|
||||
|
||||
// Initialize coefficients
|
||||
torch::Tensor coef = torch::zeros({ n_features }, torch::kFloat32);
|
||||
double intercept = 0.0;
|
||||
|
||||
// Ensure consistent types
|
||||
torch::Tensor weights = sampleWeights.to(torch::kFloat32);
|
||||
|
||||
// Learning rate (can be adaptive)
|
||||
double learningRate = 0.01;
|
||||
|
||||
// Proximal gradient descent
|
||||
for (int iter = 0; iter < maxIter; ++iter) {
|
||||
// Compute predictions
|
||||
torch::Tensor linearPred = X.matmul(coef);
|
||||
if (fitIntercept) {
|
||||
linearPred = linearPred + intercept;
|
||||
}
|
||||
torch::Tensor pred = sigmoid(linearPred);
|
||||
|
||||
// Compute gradient
|
||||
torch::Tensor diff = pred - y;
|
||||
torch::Tensor grad = X.t().matmul(diff * weights) / n_samples;
|
||||
|
||||
// Gradient descent step
|
||||
torch::Tensor coef_new = coef - learningRate * grad;
|
||||
|
||||
// Proximal step (soft thresholding)
|
||||
for (int j = 0; j < n_features; ++j) {
|
||||
coef_new[j] = softThreshold(coef_new[j].item<float>(),
|
||||
learningRate * alpha);
|
||||
}
|
||||
|
||||
// Update intercept if needed
|
||||
if (fitIntercept) {
|
||||
double grad_intercept = (diff * weights).sum().item<float>() / n_samples;
|
||||
intercept -= learningRate * grad_intercept;
|
||||
}
|
||||
|
||||
// Check convergence
|
||||
double change = (coef_new - coef).abs().max().item<float>();
|
||||
coef = coef_new;
|
||||
|
||||
if (change < tolerance) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Adaptive learning rate (optional)
|
||||
if (iter % 100 == 0) {
|
||||
learningRate *= 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
// Store final coefficients
|
||||
coefficients.resize(n_features);
|
||||
for (int j = 0; j < n_features; ++j) {
|
||||
coefficients[j] = coef[j].item<float>();
|
||||
}
|
||||
}
|
||||
|
||||
double L1FS::softThreshold(double x, double lambda) const
|
||||
{
|
||||
if (x > lambda) {
|
||||
return x - lambda;
|
||||
} else if (x < -lambda) {
|
||||
return x + lambda;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
torch::Tensor L1FS::sigmoid(const torch::Tensor& z) const
|
||||
{
|
||||
return 1.0 / (1.0 + torch::exp(-z));
|
||||
}
|
||||
|
||||
std::vector<double> L1FS::getCoefficients() const
|
||||
{
|
||||
if (!fitted) {
|
||||
throw std::runtime_error("L1FS not fitted");
|
||||
}
|
||||
return coefficients;
|
||||
}
|
||||
|
||||
} // namespace bayesnet
|
||||
83
bayesnet/feature_selection/L1FS.h
Normal file
83
bayesnet/feature_selection/L1FS.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// ***************************************************************
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Ricardo Montañana Gómez
|
||||
// SPDX-FileType: SOURCE
|
||||
// SPDX-License-Identifier: MIT
|
||||
// ***************************************************************
|
||||
|
||||
#ifndef L1FS_H
|
||||
#define L1FS_H
|
||||
#include <torch/torch.h>
|
||||
#include <vector>
|
||||
#include "bayesnet/feature_selection/FeatureSelect.h"
|
||||
|
||||
namespace bayesnet {
|
||||
/**
|
||||
* L1-Regularized Feature Selection (L1FS)
|
||||
*
|
||||
* This class implements feature selection using L1-regularized linear models.
|
||||
* For classification tasks, it uses one-vs-rest logistic regression with L1 penalty.
|
||||
* For regression tasks, it uses Lasso regression.
|
||||
*
|
||||
* The L1 penalty induces sparsity in the model coefficients, effectively
|
||||
* performing feature selection by setting irrelevant feature weights to zero.
|
||||
*/
|
||||
class L1FS : public FeatureSelect {
|
||||
public:
|
||||
/**
|
||||
* Constructor for L1FS
|
||||
* @param samples n+1xm tensor where samples[-1] is the target variable
|
||||
* @param features vector of feature names
|
||||
* @param className name of the class/target variable
|
||||
* @param maxFeatures maximum number of features to select (0 = all)
|
||||
* @param classNumStates number of states for classification (ignored for regression)
|
||||
* @param weights sample weights
|
||||
* @param alpha L1 regularization strength (higher = more sparsity)
|
||||
* @param maxIter maximum iterations for optimization
|
||||
* @param tolerance convergence tolerance
|
||||
* @param fitIntercept whether to fit an intercept term
|
||||
*/
|
||||
L1FS(const torch::Tensor& samples,
|
||||
const std::vector<std::string>& features,
|
||||
const std::string& className,
|
||||
const int maxFeatures,
|
||||
const int classNumStates,
|
||||
const torch::Tensor& weights,
|
||||
const double alpha = 1.0,
|
||||
const int maxIter = 1000,
|
||||
const double tolerance = 1e-4,
|
||||
const bool fitIntercept = true);
|
||||
|
||||
virtual ~L1FS() {};
|
||||
|
||||
void fit() override;
|
||||
|
||||
// Get the learned coefficients for each feature
|
||||
std::vector<double> getCoefficients() const;
|
||||
|
||||
private:
|
||||
double alpha; // L1 regularization strength
|
||||
int maxIter; // Maximum iterations for optimization
|
||||
double tolerance; // Convergence tolerance
|
||||
bool fitIntercept; // Whether to fit intercept
|
||||
bool isRegression; // Task type (regression vs classification)
|
||||
|
||||
std::vector<double> coefficients; // Learned coefficients
|
||||
|
||||
// Coordinate descent for Lasso regression
|
||||
void fitLasso(const torch::Tensor& X, const torch::Tensor& y, const torch::Tensor& sampleWeights);
|
||||
|
||||
// Proximal gradient descent for L1-regularized logistic regression
|
||||
void fitL1Logistic(const torch::Tensor& X, const torch::Tensor& y, const torch::Tensor& sampleWeights);
|
||||
|
||||
// Soft thresholding operator for L1 regularization
|
||||
double softThreshold(double x, double lambda) const;
|
||||
|
||||
// Logistic function
|
||||
torch::Tensor sigmoid(const torch::Tensor& z) const;
|
||||
|
||||
// Compute logistic loss
|
||||
double logisticLoss(const torch::Tensor& X, const torch::Tensor& y,
|
||||
const torch::Tensor& coef, const torch::Tensor& sampleWeights) const;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
// ***************************************************************
|
||||
|
||||
#include "Node.h"
|
||||
#include <iterator>
|
||||
|
||||
namespace bayesnet {
|
||||
|
||||
@@ -94,39 +95,51 @@ namespace bayesnet {
|
||||
{
|
||||
dimensions.clear();
|
||||
dimensions.reserve(parents.size() + 1);
|
||||
// Get dimensions of the CPT
|
||||
dimensions.push_back(numStates);
|
||||
for (const auto& parent : parents) {
|
||||
dimensions.push_back(parent->getNumStates());
|
||||
}
|
||||
//transform(parents.begin(), parents.end(), back_inserter(dimensions), [](const auto& parent) { return parent->getNumStates(); });
|
||||
// Create a tensor initialized with smoothing
|
||||
cpTable = torch::full(dimensions, smoothing, torch::kDouble);
|
||||
// Create a map for quick feature index lookup
|
||||
|
||||
// Build feature index map
|
||||
std::unordered_map<std::string, int> featureIndexMap;
|
||||
for (size_t i = 0; i < features.size(); ++i) {
|
||||
featureIndexMap[features[i]] = i;
|
||||
}
|
||||
// Fill table with counts
|
||||
// Get the index of this node's feature
|
||||
int name_index = featureIndexMap[name];
|
||||
// Get parent indices in dataset
|
||||
std::vector<int> parent_indices;
|
||||
parent_indices.reserve(parents.size());
|
||||
|
||||
// Gather indices for node and parents
|
||||
std::vector<int64_t> all_indices;
|
||||
all_indices.push_back(featureIndexMap[name]);
|
||||
for (const auto& parent : parents) {
|
||||
parent_indices.push_back(featureIndexMap[parent->getName()]);
|
||||
all_indices.push_back(featureIndexMap[parent->getName()]);
|
||||
}
|
||||
c10::List<c10::optional<at::Tensor>> coordinates;
|
||||
for (int n_sample = 0; n_sample < dataset.size(1); ++n_sample) {
|
||||
coordinates.clear();
|
||||
auto sample = dataset.index({ "...", n_sample });
|
||||
coordinates.push_back(sample[name_index]);
|
||||
for (size_t i = 0; i < parent_indices.size(); ++i) {
|
||||
coordinates.push_back(sample[parent_indices[i]]);
|
||||
|
||||
// Extract relevant columns: shape (num_features, num_samples)
|
||||
auto indices_tensor = dataset.index_select(0, torch::tensor(all_indices, torch::kLong));
|
||||
indices_tensor = indices_tensor.transpose(0, 1).to(torch::kLong); // (num_samples, num_features)
|
||||
|
||||
// Manual flattening of indices
|
||||
std::vector<int64_t> strides(all_indices.size(), 1);
|
||||
for (int i = strides.size() - 2; i >= 0; --i) {
|
||||
strides[i] = strides[i + 1] * cpTable.size(i + 1);
|
||||
}
|
||||
// Increment the count of the corresponding coordinate
|
||||
cpTable.index_put_({ coordinates }, weights.index({ n_sample }), true);
|
||||
auto indices_tensor_cpu = indices_tensor.cpu();
|
||||
auto indices_accessor = indices_tensor_cpu.accessor<int64_t, 2>();
|
||||
std::vector<int64_t> flat_indices(indices_tensor.size(0));
|
||||
for (int64_t i = 0; i < indices_tensor.size(0); ++i) {
|
||||
int64_t idx = 0;
|
||||
for (size_t j = 0; j < strides.size(); ++j) {
|
||||
idx += indices_accessor[i][j] * strides[j];
|
||||
}
|
||||
flat_indices[i] = idx;
|
||||
}
|
||||
|
||||
// Accumulate weights into flat CPT
|
||||
auto flat_cpt = cpTable.flatten();
|
||||
auto flat_indices_tensor = torch::from_blob(flat_indices.data(), { (int64_t)flat_indices.size() }, torch::kLong).clone();
|
||||
flat_cpt.index_add_(0, flat_indices_tensor, weights.cpu());
|
||||
cpTable = flat_cpt.view(cpTable.sizes());
|
||||
|
||||
// Normalize the counts (dividing each row by the sum of the row)
|
||||
cpTable /= cpTable.sum(0, true);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
#include <condition_variable>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
class CountingSemaphore {
|
||||
public:
|
||||
|
||||
4
bayesnetConfig.cmake.in
Normal file
4
bayesnetConfig.cmake.in
Normal file
@@ -0,0 +1,4 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/bayesnetTargets.cmake")
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
function(add_git_submodule dir)
|
||||
find_package(Git REQUIRED)
|
||||
|
||||
if(NOT EXISTS ${dir}/CMakeLists.txt)
|
||||
message(STATUS "🚨 Adding git submodule => ${dir}")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE}
|
||||
submodule update --init --recursive -- ${dir}
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
add_subdirectory(${dir})
|
||||
endfunction(add_git_submodule)
|
||||
@@ -1,746 +0,0 @@
|
||||
# Copyright (c) 2012 - 2017, Lars Bilke
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# CHANGES:
|
||||
#
|
||||
# 2012-01-31, Lars Bilke
|
||||
# - Enable Code Coverage
|
||||
#
|
||||
# 2013-09-17, Joakim Söderberg
|
||||
# - Added support for Clang.
|
||||
# - Some additional usage instructions.
|
||||
#
|
||||
# 2016-02-03, Lars Bilke
|
||||
# - Refactored functions to use named parameters
|
||||
#
|
||||
# 2017-06-02, Lars Bilke
|
||||
# - Merged with modified version from github.com/ufz/ogs
|
||||
#
|
||||
# 2019-05-06, Anatolii Kurotych
|
||||
# - Remove unnecessary --coverage flag
|
||||
#
|
||||
# 2019-12-13, FeRD (Frank Dana)
|
||||
# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
|
||||
# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
|
||||
# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
|
||||
# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
|
||||
# - Set lcov basedir with -b argument
|
||||
# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
|
||||
# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
|
||||
# - Delete output dir, .info file on 'make clean'
|
||||
# - Remove Python detection, since version mismatches will break gcovr
|
||||
# - Minor cleanup (lowercase function names, update examples...)
|
||||
#
|
||||
# 2019-12-19, FeRD (Frank Dana)
|
||||
# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
|
||||
#
|
||||
# 2020-01-19, Bob Apthorpe
|
||||
# - Added gfortran support
|
||||
#
|
||||
# 2020-02-17, FeRD (Frank Dana)
|
||||
# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
|
||||
# in EXCLUDEs, and remove manual escaping from gcovr targets
|
||||
#
|
||||
# 2021-01-19, Robin Mueller
|
||||
# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
|
||||
# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
|
||||
# flags to the gcovr command
|
||||
#
|
||||
# 2020-05-04, Mihchael Davis
|
||||
# - Add -fprofile-abs-path to make gcno files contain absolute paths
|
||||
# - Fix BASE_DIRECTORY not working when defined
|
||||
# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
|
||||
#
|
||||
# 2021-05-10, Martin Stump
|
||||
# - Check if the generator is multi-config before warning about non-Debug builds
|
||||
#
|
||||
# 2022-02-22, Marko Wehle
|
||||
# - Change gcovr output from -o <filename> for --xml <filename> and --html <filename> output respectively.
|
||||
# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt".
|
||||
#
|
||||
# 2022-09-28, Sebastian Mueller
|
||||
# - fix append_coverage_compiler_flags_to_target to correctly add flags
|
||||
# - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent)
|
||||
#
|
||||
# USAGE:
|
||||
#
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
#
|
||||
# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
|
||||
# using a CMake option() to enable it just optionally):
|
||||
# include(CodeCoverage)
|
||||
#
|
||||
# 3. Append necessary compiler flags for all supported source files:
|
||||
# append_coverage_compiler_flags()
|
||||
# Or for specific target:
|
||||
# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME)
|
||||
#
|
||||
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
|
||||
#
|
||||
# 4. If you need to exclude additional directories from the report, specify them
|
||||
# using full paths in the COVERAGE_EXCLUDES variable before calling
|
||||
# setup_target_for_coverage_*().
|
||||
# Example:
|
||||
# set(COVERAGE_EXCLUDES
|
||||
# '${PROJECT_SOURCE_DIR}/src/dir1/*'
|
||||
# '/path/to/my/src/dir2/*')
|
||||
# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
|
||||
# Example:
|
||||
# setup_target_for_coverage_lcov(
|
||||
# NAME coverage
|
||||
# EXECUTABLE testrunner
|
||||
# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
|
||||
#
|
||||
# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
|
||||
# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
|
||||
# Example:
|
||||
# set(COVERAGE_EXCLUDES "dir1/*")
|
||||
# setup_target_for_coverage_gcovr_html(
|
||||
# NAME coverage
|
||||
# EXECUTABLE testrunner
|
||||
# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
|
||||
# EXCLUDE "dir2/*")
|
||||
#
|
||||
# 5. Use the functions described below to create a custom make target which
|
||||
# runs your test executable and produces a code coverage report.
|
||||
#
|
||||
# 6. Build a Debug build:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
# make my_coverage_target
|
||||
#
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
option(CODE_COVERAGE_VERBOSE "Verbose information" TRUE)
|
||||
|
||||
# Check prereqs
|
||||
find_program( GCOV_PATH gcov )
|
||||
find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
|
||||
find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
|
||||
find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
|
||||
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
|
||||
find_program( CPPFILT_PATH NAMES c++filt )
|
||||
|
||||
if(NOT GCOV_PATH)
|
||||
message(FATAL_ERROR "gcov not found! Aborting...")
|
||||
endif() # NOT GCOV_PATH
|
||||
|
||||
# Check supported compiler (Clang, GNU and Flang)
|
||||
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
|
||||
foreach(LANG ${LANGUAGES})
|
||||
if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
|
||||
if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
|
||||
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
|
||||
endif()
|
||||
elseif(NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU"
|
||||
AND NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(LLVM)?[Ff]lang")
|
||||
if ("${LANG}" MATCHES "CUDA")
|
||||
message(STATUS "Ignoring CUDA")
|
||||
else()
|
||||
message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(COVERAGE_COMPILER_FLAGS "-g --coverage"
|
||||
CACHE INTERNAL "")
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
|
||||
if(HAVE_fprofile_abs_path)
|
||||
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_Fortran_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_C_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used for linking binaries during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
|
||||
FORCE )
|
||||
mark_as_advanced(
|
||||
CMAKE_Fortran_FLAGS_COVERAGE
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
|
||||
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
|
||||
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
|
||||
link_libraries(gcov)
|
||||
endif()
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_lcov(
|
||||
# NAME testrunner_coverage # New target name
|
||||
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES testrunner # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||
# NO_DEMANGLE # Don't demangle C++ symbols
|
||||
# # even if c++filt is found
|
||||
# )
|
||||
function(setup_target_for_coverage_lcov)
|
||||
|
||||
set(options NO_DEMANGLE SONARQUBE)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT LCOV_PATH)
|
||||
message(FATAL_ERROR "lcov not found! Aborting...")
|
||||
endif() # NOT LCOV_PATH
|
||||
|
||||
if(NOT GENHTML_PATH)
|
||||
message(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
endif() # NOT GENHTML_PATH
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||
set(LCOV_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||
endif()
|
||||
list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES LCOV_EXCLUDES)
|
||||
|
||||
# Conditional arguments
|
||||
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
|
||||
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
|
||||
endif()
|
||||
|
||||
# Setting up commands which will be run to generate coverage data.
|
||||
# Cleanup lcov
|
||||
set(LCOV_CLEAN_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory .
|
||||
-b ${BASEDIR} --zerocounters
|
||||
)
|
||||
# Create baseline to make sure untouched files show up in the report
|
||||
set(LCOV_BASELINE_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b
|
||||
${BASEDIR} -o ${Coverage_NAME}.base
|
||||
)
|
||||
# Run tests
|
||||
set(LCOV_EXEC_TESTS_CMD
|
||||
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
)
|
||||
# Capturing lcov counters and generating report
|
||||
set(LCOV_CAPTURE_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b
|
||||
${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
|
||||
)
|
||||
# add baseline counters
|
||||
set(LCOV_BASELINE_COUNT_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base
|
||||
-a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
|
||||
)
|
||||
# filter collected data to final coverage report
|
||||
set(LCOV_FILTER_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove
|
||||
${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
|
||||
)
|
||||
# Generate HTML output
|
||||
set(LCOV_GEN_HTML_CMD
|
||||
${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
|
||||
${Coverage_NAME} ${Coverage_NAME}.info
|
||||
)
|
||||
if(${Coverage_SONARQUBE})
|
||||
# Generate SonarQube output
|
||||
set(GCOVR_XML_CMD
|
||||
${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
|
||||
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
|
||||
)
|
||||
set(GCOVR_XML_CMD_COMMAND
|
||||
COMMAND ${GCOVR_XML_CMD}
|
||||
)
|
||||
set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml)
|
||||
set(GCOVR_XML_CMD_COMMENT COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.")
|
||||
endif()
|
||||
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Executed command report")
|
||||
message(STATUS "Command to clean up lcov: ")
|
||||
string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
|
||||
message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to create baseline: ")
|
||||
string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
|
||||
message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to run the tests: ")
|
||||
string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
|
||||
message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to capture counters and generate report: ")
|
||||
string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
|
||||
message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to add baseline counters: ")
|
||||
string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
|
||||
message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to filter collected data: ")
|
||||
string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
|
||||
message(STATUS "${LCOV_FILTER_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to generate lcov HTML output: ")
|
||||
string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
|
||||
message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
|
||||
|
||||
if(${Coverage_SONARQUBE})
|
||||
message(STATUS "Command to generate SonarQube XML output: ")
|
||||
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
|
||||
message(STATUS "${GCOVR_XML_CMD_SPACED}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Setup target
|
||||
add_custom_target(${Coverage_NAME}
|
||||
COMMAND ${LCOV_CLEAN_CMD}
|
||||
COMMAND ${LCOV_BASELINE_CMD}
|
||||
COMMAND ${LCOV_EXEC_TESTS_CMD}
|
||||
COMMAND ${LCOV_CAPTURE_CMD}
|
||||
COMMAND ${LCOV_BASELINE_COUNT_CMD}
|
||||
COMMAND ${LCOV_FILTER_CMD}
|
||||
COMMAND ${LCOV_GEN_HTML_CMD}
|
||||
${GCOVR_XML_CMD_COMMAND}
|
||||
|
||||
# Set output files as GENERATED (will be removed on 'make clean')
|
||||
BYPRODUCTS
|
||||
${Coverage_NAME}.base
|
||||
${Coverage_NAME}.capture
|
||||
${Coverage_NAME}.total
|
||||
${Coverage_NAME}.info
|
||||
${GCOVR_XML_CMD_BYPRODUCTS}
|
||||
${Coverage_NAME}/index.html
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
# Show where to find the lcov info report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
|
||||
${GCOVR_XML_CMD_COMMENT}
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
endfunction() # setup_target_for_coverage_lcov
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_gcovr_xml(
|
||||
# NAME ctest_coverage # New target name
|
||||
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES executable_target # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||
# )
|
||||
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
|
||||
# GCVOR command.
|
||||
function(setup_target_for_coverage_gcovr_xml)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT GCOVR_PATH)
|
||||
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
endif() # NOT GCOVR_PATH
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||
set(GCOVR_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||
endif()
|
||||
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
|
||||
|
||||
# Combine excludes to several -e arguments
|
||||
set(GCOVR_EXCLUDE_ARGS "")
|
||||
foreach(EXCLUDE ${GCOVR_EXCLUDES})
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
|
||||
endforeach()
|
||||
|
||||
# Set up commands which will be run to generate coverage data
|
||||
# Run tests
|
||||
set(GCOVR_XML_EXEC_TESTS_CMD
|
||||
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
)
|
||||
# Running gcovr
|
||||
set(GCOVR_XML_CMD
|
||||
${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
|
||||
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
|
||||
)
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Executed command report")
|
||||
|
||||
message(STATUS "Command to run tests: ")
|
||||
string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
|
||||
message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to generate gcovr XML coverage data: ")
|
||||
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
|
||||
message(STATUS "${GCOVR_XML_CMD_SPACED}")
|
||||
endif()
|
||||
|
||||
add_custom_target(${Coverage_NAME}
|
||||
COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
|
||||
COMMAND ${GCOVR_XML_CMD}
|
||||
|
||||
BYPRODUCTS ${Coverage_NAME}.xml
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Running gcovr to produce Cobertura code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
|
||||
)
|
||||
endfunction() # setup_target_for_coverage_gcovr_xml
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_gcovr_html(
|
||||
# NAME ctest_coverage # New target name
|
||||
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES executable_target # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||
# )
|
||||
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
|
||||
# GCVOR command.
|
||||
function(setup_target_for_coverage_gcovr_html)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT GCOVR_PATH)
|
||||
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
endif() # NOT GCOVR_PATH
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||
set(GCOVR_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||
endif()
|
||||
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
|
||||
|
||||
# Combine excludes to several -e arguments
|
||||
set(GCOVR_EXCLUDE_ARGS "")
|
||||
foreach(EXCLUDE ${GCOVR_EXCLUDES})
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
|
||||
endforeach()
|
||||
|
||||
# Set up commands which will be run to generate coverage data
|
||||
# Run tests
|
||||
set(GCOVR_HTML_EXEC_TESTS_CMD
|
||||
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
)
|
||||
# Create folder
|
||||
set(GCOVR_HTML_FOLDER_CMD
|
||||
${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
|
||||
)
|
||||
# Running gcovr
|
||||
set(GCOVR_HTML_CMD
|
||||
${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
|
||||
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
|
||||
)
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Executed command report")
|
||||
|
||||
message(STATUS "Command to run tests: ")
|
||||
string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
|
||||
message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to create a folder: ")
|
||||
string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
|
||||
message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to generate gcovr HTML coverage data: ")
|
||||
string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
|
||||
message(STATUS "${GCOVR_HTML_CMD_SPACED}")
|
||||
endif()
|
||||
|
||||
add_custom_target(${Coverage_NAME}
|
||||
COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
|
||||
COMMAND ${GCOVR_HTML_FOLDER_CMD}
|
||||
COMMAND ${GCOVR_HTML_CMD}
|
||||
|
||||
BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Running gcovr to produce HTML code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
endfunction() # setup_target_for_coverage_gcovr_html
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_fastcov(
|
||||
# NAME testrunner_coverage # New target name
|
||||
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES testrunner # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
|
||||
# NO_DEMANGLE # Don't demangle C++ symbols
|
||||
# # even if c++filt is found
|
||||
# SKIP_HTML # Don't create html report
|
||||
# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths
|
||||
# )
|
||||
function(setup_target_for_coverage_fastcov)
|
||||
|
||||
set(options NO_DEMANGLE SKIP_HTML)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT FASTCOV_PATH)
|
||||
message(FATAL_ERROR "fastcov not found! Aborting...")
|
||||
endif()
|
||||
|
||||
if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH)
|
||||
message(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
endif()
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (Patterns, not paths, for fastcov)
|
||||
set(FASTCOV_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
|
||||
list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
|
||||
|
||||
# Conditional arguments
|
||||
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
|
||||
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
|
||||
endif()
|
||||
|
||||
# Set up commands which will be run to generate coverage data
|
||||
set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
|
||||
|
||||
set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
|
||||
--search-directory ${BASEDIR}
|
||||
--process-gcno
|
||||
--output ${Coverage_NAME}.json
|
||||
--exclude ${FASTCOV_EXCLUDES}
|
||||
)
|
||||
|
||||
set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH}
|
||||
-C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info
|
||||
)
|
||||
|
||||
if(Coverage_SKIP_HTML)
|
||||
set(FASTCOV_HTML_CMD ";")
|
||||
else()
|
||||
set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
|
||||
-o ${Coverage_NAME} ${Coverage_NAME}.info
|
||||
)
|
||||
endif()
|
||||
|
||||
set(FASTCOV_POST_CMD ";")
|
||||
if(Coverage_POST_CMD)
|
||||
set(FASTCOV_POST_CMD ${Coverage_POST_CMD})
|
||||
endif()
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
|
||||
|
||||
message(" Running tests:")
|
||||
string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
|
||||
message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(" Capturing fastcov counters and generating report:")
|
||||
string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
|
||||
message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
|
||||
|
||||
message(" Converting fastcov .json to lcov .info:")
|
||||
string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}")
|
||||
message(" ${FASTCOV_CONVERT_CMD_SPACED}")
|
||||
|
||||
if(NOT Coverage_SKIP_HTML)
|
||||
message(" Generating HTML report: ")
|
||||
string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
|
||||
message(" ${FASTCOV_HTML_CMD_SPACED}")
|
||||
endif()
|
||||
if(Coverage_POST_CMD)
|
||||
message(" Running post command: ")
|
||||
string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}")
|
||||
message(" ${FASTCOV_POST_CMD_SPACED}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Setup target
|
||||
add_custom_target(${Coverage_NAME}
|
||||
|
||||
# Cleanup fastcov
|
||||
COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
|
||||
--search-directory ${BASEDIR}
|
||||
--zerocounters
|
||||
|
||||
COMMAND ${FASTCOV_EXEC_TESTS_CMD}
|
||||
COMMAND ${FASTCOV_CAPTURE_CMD}
|
||||
COMMAND ${FASTCOV_CONVERT_CMD}
|
||||
COMMAND ${FASTCOV_HTML_CMD}
|
||||
COMMAND ${FASTCOV_POST_CMD}
|
||||
|
||||
# Set output files as GENERATED (will be removed on 'make clean')
|
||||
BYPRODUCTS
|
||||
${Coverage_NAME}.info
|
||||
${Coverage_NAME}.json
|
||||
${Coverage_NAME}/index.html # report directory
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.")
|
||||
if(NOT Coverage_SKIP_HTML)
|
||||
string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
|
||||
endif()
|
||||
# Show where to find the fastcov info report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
|
||||
)
|
||||
|
||||
endfunction() # setup_target_for_coverage_fastcov
|
||||
|
||||
function(append_coverage_compiler_flags)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
|
||||
endfunction() # append_coverage_compiler_flags
|
||||
|
||||
# Setup coverage for specific library
|
||||
function(append_coverage_compiler_flags_to_target name)
|
||||
separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}")
|
||||
target_compile_options(${name} PRIVATE ${_flag_list})
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
|
||||
target_link_libraries(${name} PRIVATE gcov)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,22 +0,0 @@
|
||||
if(ENABLE_CLANG_TIDY)
|
||||
find_program(CLANG_TIDY_COMMAND NAMES clang-tidy)
|
||||
|
||||
if(NOT CLANG_TIDY_COMMAND)
|
||||
message(WARNING "🔴 CMake_RUN_CLANG_TIDY is ON but clang-tidy is not found!")
|
||||
set(CMAKE_CXX_CLANG_TIDY "" CACHE STRING "" FORCE)
|
||||
else()
|
||||
|
||||
message(STATUS "🟢 CMake_RUN_CLANG_TIDY is ON")
|
||||
set(CLANGTIDY_EXTRA_ARGS
|
||||
"-extra-arg=-Wno-unknown-warning-option"
|
||||
)
|
||||
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND};-p=${CMAKE_BINARY_DIR};${CLANGTIDY_EXTRA_ARGS}" CACHE STRING "" FORCE)
|
||||
|
||||
add_custom_target(clang-tidy
|
||||
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ${CMAKE_PROJECT_NAME}
|
||||
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clang-tidy
|
||||
COMMENT "Running clang-tidy..."
|
||||
)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
endif()
|
||||
endif(ENABLE_CLANG_TIDY)
|
||||
10
conandata.yml
Normal file
10
conandata.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
sources:
|
||||
"1.1.2":
|
||||
url: "https://github.com/rmontanana/BayesNet/archive/v1.1.2.tar.gz"
|
||||
sha256: "placeholder_sha256" # Replace with actual SHA256 when releasing
|
||||
"1.0.7":
|
||||
url: "https://github.com/rmontanana/BayesNet/archive/v1.0.7.tar.gz"
|
||||
sha256: "placeholder_sha256" # Replace with actual SHA256 when releasing
|
||||
|
||||
patches:
|
||||
# Add patches here if needed for specific versions
|
||||
93
conanfile.py
Normal file
93
conanfile.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import os, re, pathlib
|
||||
from conan import ConanFile
|
||||
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps
|
||||
from conan.tools.files import copy
|
||||
|
||||
class BayesNetConan(ConanFile):
|
||||
name = "bayesnet"
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
options = {
|
||||
"shared": [True, False],
|
||||
"fPIC": [True, False],
|
||||
"enable_testing": [True, False],
|
||||
"enable_coverage": [True, False]
|
||||
}
|
||||
default_options = {
|
||||
"shared": False,
|
||||
"fPIC": True,
|
||||
"enable_testing": False,
|
||||
"enable_coverage": False
|
||||
}
|
||||
|
||||
# Sources are located in the same place as this recipe, copy them to the recipe
|
||||
exports_sources = "CMakeLists.txt", "bayesnet/*", "config/*", "cmake/*", "docs/*", "tests/*", "bayesnetConfig.cmake.in"
|
||||
|
||||
def set_version(self) -> None:
|
||||
cmake = pathlib.Path(self.recipe_folder) / "CMakeLists.txt"
|
||||
text = cmake.read_text(encoding="utf-8")
|
||||
|
||||
# Accept either: project(foo VERSION 1.2.3) or set(foo_VERSION 1.2.3)
|
||||
match = re.search(
|
||||
r"""project\s*\([^\)]*VERSION\s+([0-9]+\.[0-9]+\.[0-9]+)""",
|
||||
text, re.IGNORECASE | re.VERBOSE
|
||||
)
|
||||
if match:
|
||||
self.version = match.group(1)
|
||||
else:
|
||||
raise Exception("Version not found in CMakeLists.txt")
|
||||
self.version = match.group(1)
|
||||
|
||||
def config_options(self):
|
||||
if self.settings.os == "Windows":
|
||||
del self.options.fPIC
|
||||
|
||||
def configure(self):
|
||||
if self.options.shared:
|
||||
self.options.rm_safe("fPIC")
|
||||
|
||||
def requirements(self):
|
||||
# Core dependencies
|
||||
self.requires("libtorch/2.7.0")
|
||||
self.requires("nlohmann_json/3.11.3")
|
||||
self.requires("folding/1.1.1") # Custom package
|
||||
self.requires("fimdlp/2.1.0") # Custom package
|
||||
|
||||
def build_requirements(self):
|
||||
self.build_requires("cmake/[>=3.27]")
|
||||
self.test_requires("arff-files/1.2.0") # Custom package
|
||||
self.test_requires("catch2/3.8.1")
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self)
|
||||
|
||||
def generate(self):
|
||||
deps = CMakeDeps(self)
|
||||
deps.generate()
|
||||
tc = CMakeToolchain(self)
|
||||
tc.variables["ENABLE_TESTING"] = self.options.enable_testing
|
||||
tc.variables["CODE_COVERAGE"] = self.options.enable_coverage
|
||||
tc.generate()
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
|
||||
if self.options.enable_testing:
|
||||
# Run tests only if we're building with testing enabled
|
||||
self.run("ctest --output-on-failure", cwd=self.build_folder)
|
||||
|
||||
def package(self):
|
||||
copy(self, "LICENSE", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))
|
||||
cmake = CMake(self)
|
||||
cmake.install()
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.libs = ["bayesnet"]
|
||||
self.cpp_info.includedirs = ["include"]
|
||||
self.cpp_info.set_property("cmake_find_mode", "both")
|
||||
self.cpp_info.set_property("cmake_target_name", "bayesnet::bayesnet")
|
||||
|
||||
# Add compiler flags that might be needed
|
||||
if self.settings.os == "Linux":
|
||||
self.cpp_info.system_libs = ["pthread"]
|
||||
@@ -3,12 +3,8 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR @
|
||||
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR @
|
||||
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH @
|
||||
|
||||
static constexpr std::string_view project_name = "@PROJECT_NAME@";
|
||||
static constexpr std::string_view project_version = "@PROJECT_VERSION@";
|
||||
static constexpr std::string_view project_description = "@PROJECT_DESCRIPTION@";
|
||||
static constexpr std::string_view git_sha = "@GIT_SHA@";
|
||||
static constexpr std::string_view data_path = "@BayesNet_SOURCE_DIR@/tests/data/";
|
||||
static constexpr std::string_view data_path = "@bayesnet_SOURCE_DIR@/tests/data/";
|
||||
@@ -96,6 +96,8 @@ abstract C_0002617087915615796317 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
{abstract} +topological_order() = 0 : std::vector<std::string>
|
||||
{abstract} #trainModel(const torch::Tensor & weights, const Smoothing_t smoothing) = 0 : void
|
||||
__
|
||||
#notes : std::vector<std::string>
|
||||
#status : status_t
|
||||
#validHyperparameters : std::vector<std::string>
|
||||
}
|
||||
class "bayesnet::Metrics" as C_0005895723015084986588
|
||||
@@ -153,6 +155,7 @@ abstract C_0016351972983202413152 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+topological_order() : std::vector<std::string>
|
||||
#trainModel(const torch::Tensor & weights, const Smoothing_t smoothing) : void
|
||||
__
|
||||
#CLASSIFIER_NOT_FITTED : const std::string
|
||||
#className : std::string
|
||||
#dataset : torch::Tensor
|
||||
#features : std::vector<std::string>
|
||||
@@ -161,20 +164,44 @@ __
|
||||
#metrics : Metrics
|
||||
#model : Network
|
||||
#n : unsigned int
|
||||
#notes : std::vector<std::string>
|
||||
#states : std::map<std::string,std::vector<int>>
|
||||
#status : status_t
|
||||
}
|
||||
class "bayesnet::Proposal" as C_0017759964713298103839
|
||||
class C_0017759964713298103839 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+Proposal(torch::Tensor & pDataset, std::vector<std::string> & features_, std::string & className_) : void
|
||||
+~Proposal() : void
|
||||
..
|
||||
#checkInput(const torch::Tensor & X, const torch::Tensor & y) : void
|
||||
#fit_local_discretization(const torch::Tensor & y) : std::map<std::string,std::vector<int>>
|
||||
#localDiscretizationProposal(const std::map<std::string,std::vector<int>> & states, Network & model) : std::map<std::string,std::vector<int>>
|
||||
#prepareX(torch::Tensor & X) : torch::Tensor
|
||||
__
|
||||
#Xf : torch::Tensor
|
||||
#discretizers : map<std::string,mdlp::CPPFImdlp *>
|
||||
#y : torch::Tensor
|
||||
}
|
||||
class "bayesnet::KDB" as C_0008902920152122000044
|
||||
class C_0008902920152122000044 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+KDB(int k, float theta = 0.03) : void
|
||||
+~KDB() = default : void
|
||||
..
|
||||
#add_m_edges(int idx, std::vector<int> & S, torch::Tensor & weights) : void
|
||||
#buildModel(const torch::Tensor & weights) : void
|
||||
+graph(const std::string & name = "KDB") const : std::vector<std::string>
|
||||
+setHyperparameters(const nlohmann::json & hyperparameters_) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::KDBLd" as C_0002756018222998454702
|
||||
class C_0002756018222998454702 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+KDBLd(int k) : void
|
||||
+~KDBLd() = default : void
|
||||
..
|
||||
+fit(torch::Tensor & X, torch::Tensor & y, const std::vector<std::string> & features, const std::string & className, std::map<std::string,std::vector<int>> & states, const Smoothing_t smoothing) : KDBLd &
|
||||
+graph(const std::string & name = "KDB") const : std::vector<std::string>
|
||||
+predict(torch::Tensor & X) : torch::Tensor
|
||||
{static} +version() : std::string
|
||||
__
|
||||
}
|
||||
class "bayesnet::SPODE" as C_0004096182510460307610
|
||||
class C_0004096182510460307610 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+SPODE(int root) : void
|
||||
@@ -182,6 +209,20 @@ class C_0004096182510460307610 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
..
|
||||
#buildModel(const torch::Tensor & weights) : void
|
||||
+graph(const std::string & name = "SPODE") const : std::vector<std::string>
|
||||
+setHyperparameters(const nlohmann::json & hyperparameters_) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::SPODELd" as C_0010957245114062042836
|
||||
class C_0010957245114062042836 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+SPODELd(int root) : void
|
||||
+~SPODELd() = default : void
|
||||
..
|
||||
+commonFit(const std::vector<std::string> & features, const std::string & className, std::map<std::string,std::vector<int>> & states, const Smoothing_t smoothing) : SPODELd &
|
||||
+fit(torch::Tensor & X, torch::Tensor & y, const std::vector<std::string> & features, const std::string & className, std::map<std::string,std::vector<int>> & states, const Smoothing_t smoothing) : SPODELd &
|
||||
+fit(torch::Tensor & dataset, const std::vector<std::string> & features, const std::string & className, std::map<std::string,std::vector<int>> & states, const Smoothing_t smoothing) : SPODELd &
|
||||
+graph(const std::string & name = "SPODELd") const : std::vector<std::string>
|
||||
+predict(torch::Tensor & X) : torch::Tensor
|
||||
{static} +version() : std::string
|
||||
__
|
||||
}
|
||||
class "bayesnet::SPnDE" as C_0016268916386101512883
|
||||
@@ -200,44 +241,7 @@ class C_0014087955399074584137 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
..
|
||||
#buildModel(const torch::Tensor & weights) : void
|
||||
+graph(const std::string & name = "TAN") const : std::vector<std::string>
|
||||
__
|
||||
}
|
||||
class "bayesnet::Proposal" as C_0017759964713298103839
|
||||
class C_0017759964713298103839 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+Proposal(torch::Tensor & pDataset, std::vector<std::string> & features_, std::string & className_) : void
|
||||
+~Proposal() : void
|
||||
..
|
||||
#checkInput(const torch::Tensor & X, const torch::Tensor & y) : void
|
||||
#fit_local_discretization(const torch::Tensor & y) : std::map<std::string,std::vector<int>>
|
||||
#localDiscretizationProposal(const std::map<std::string,std::vector<int>> & states, Network & model) : std::map<std::string,std::vector<int>>
|
||||
#prepareX(torch::Tensor & X) : torch::Tensor
|
||||
__
|
||||
#Xf : torch::Tensor
|
||||
#discretizers : map<std::string,mdlp::CPPFImdlp *>
|
||||
#y : torch::Tensor
|
||||
}
|
||||
class "bayesnet::KDBLd" as C_0002756018222998454702
|
||||
class C_0002756018222998454702 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+KDBLd(int k) : void
|
||||
+~KDBLd() = default : void
|
||||
..
|
||||
+fit(torch::Tensor & X, torch::Tensor & y, const std::vector<std::string> & features, const std::string & className, std::map<std::string,std::vector<int>> & states, const Smoothing_t smoothing) : KDBLd &
|
||||
+graph(const std::string & name = "KDB") const : std::vector<std::string>
|
||||
+predict(torch::Tensor & X) : torch::Tensor
|
||||
{static} +version() : std::string
|
||||
__
|
||||
}
|
||||
class "bayesnet::SPODELd" as C_0010957245114062042836
|
||||
class C_0010957245114062042836 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+SPODELd(int root) : void
|
||||
+~SPODELd() = default : void
|
||||
..
|
||||
+commonFit(const std::vector<std::string> & features, const std::string & className, std::map<std::string,std::vector<int>> & states, const Smoothing_t smoothing) : SPODELd &
|
||||
+fit(torch::Tensor & X, torch::Tensor & y, const std::vector<std::string> & features, const std::string & className, std::map<std::string,std::vector<int>> & states, const Smoothing_t smoothing) : SPODELd &
|
||||
+fit(torch::Tensor & dataset, const std::vector<std::string> & features, const std::string & className, std::map<std::string,std::vector<int>> & states, const Smoothing_t smoothing) : SPODELd &
|
||||
+graph(const std::string & name = "SPODELd") const : std::vector<std::string>
|
||||
+predict(torch::Tensor & X) : torch::Tensor
|
||||
{static} +version() : std::string
|
||||
+setHyperparameters(const nlohmann::json & hyperparameters_) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::TANLd" as C_0013350632773616302678
|
||||
@@ -250,6 +254,64 @@ class C_0013350632773616302678 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+predict(torch::Tensor & X) : torch::Tensor
|
||||
__
|
||||
}
|
||||
class "bayesnet::XSp2de" as C_0007640742442325463418
|
||||
class C_0007640742442325463418 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+XSp2de(int spIndex1, int spIndex2) : void
|
||||
..
|
||||
#buildModel(const torch::Tensor & weights) : void
|
||||
+fitx(torch::Tensor & X, torch::Tensor & y, torch::Tensor & weights_, const Smoothing_t smoothing) : void
|
||||
+getClassNumStates() const : int
|
||||
+getNFeatures() const : int
|
||||
+getNumberOfEdges() const : int
|
||||
+getNumberOfNodes() const : int
|
||||
+getNumberOfStates() const : int
|
||||
+graph(const std::string & title) const : std::vector<std::string>
|
||||
+predict(const std::vector<int> & instance) const : int
|
||||
+predict(std::vector<std::vector<int>> & test_data) : std::vector<int>
|
||||
+predict(torch::Tensor & X) : torch::Tensor
|
||||
+predict_proba(const std::vector<int> & instance) const : std::vector<double>
|
||||
+predict_proba(std::vector<std::vector<int>> & test_data) : std::vector<std::vector<double>>
|
||||
+predict_proba(torch::Tensor & X) : torch::Tensor
|
||||
+score(std::vector<std::vector<int>> & X, std::vector<int> & y) : float
|
||||
+score(torch::Tensor & X, torch::Tensor & y) : float
|
||||
+setHyperparameters(const nlohmann::json & hyperparameters_) : void
|
||||
+to_string() const : std::string
|
||||
#trainModel(const torch::Tensor & weights, const bayesnet::Smoothing_t smoothing) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::XSpode" as C_0015654113248178830206
|
||||
class C_0015654113248178830206 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+XSpode(int spIndex) : void
|
||||
..
|
||||
#buildModel(const torch::Tensor & weights) : void
|
||||
+fitx(torch::Tensor & X, torch::Tensor & y, torch::Tensor & weights_, const Smoothing_t smoothing) : void
|
||||
+getClassNumStates() const : int
|
||||
+getNFeatures() const : int
|
||||
+getNumberOfEdges() const : int
|
||||
+getNumberOfNodes() const : int
|
||||
+getNumberOfStates() const : int
|
||||
+getStates() : std::vector<int> &
|
||||
+graph(const std::string & title) const : std::vector<std::string>
|
||||
+normalize(std::vector<double> & v) const : void
|
||||
+predict(const std::vector<int> & instance) const : int
|
||||
+predict(std::vector<std::vector<int>> & X) : std::vector<int>
|
||||
+predict(torch::Tensor & X) : torch::Tensor
|
||||
+predict_proba(std::vector<std::vector<int>> & X) : std::vector<std::vector<double>>
|
||||
+predict_proba(torch::Tensor & X) : torch::Tensor
|
||||
+predict_proba(const std::vector<int> & instance) const : std::vector<double>
|
||||
+score(torch::Tensor & X, torch::Tensor & y) : float
|
||||
+score(std::vector<std::vector<int>> & X, std::vector<int> & y) : float
|
||||
+setHyperparameters(const nlohmann::json & hyperparameters_) : void
|
||||
+to_string() const : std::string
|
||||
#trainModel(const torch::Tensor & weights, const bayesnet::Smoothing_t smoothing) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::TensorUtils" as C_0010304804115474100819
|
||||
class C_0010304804115474100819 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
{static} +to_matrix(const torch::Tensor & X) : std::vector<std::vector<int>>
|
||||
{static} +to_vector<T>(const torch::Tensor & y) : std::vector<T>
|
||||
__
|
||||
}
|
||||
class "bayesnet::Ensemble" as C_0015881931090842884611
|
||||
class C_0015881931090842884611 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+Ensemble(bool predict_voting = true) : void
|
||||
@@ -302,6 +364,17 @@ class C_0006288892608974306258 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+setHyperparameters(const nlohmann::json & hyperparameters) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::AODELd" as C_0003898187834670349177
|
||||
class C_0003898187834670349177 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+AODELd(bool predict_voting = true) : void
|
||||
+~AODELd() = default : void
|
||||
..
|
||||
#buildModel(const torch::Tensor & weights) : void
|
||||
+fit(torch::Tensor & X_, torch::Tensor & y_, const std::vector<std::string> & features_, const std::string & className_, std::map<std::string,std::vector<int>> & states_, const Smoothing_t smoothing) : AODELd &
|
||||
+graph(const std::string & name = "AODELd") const : std::vector<std::string>
|
||||
#trainModel(const torch::Tensor & weights, const Smoothing_t smoothing) : void
|
||||
__
|
||||
}
|
||||
abstract "bayesnet::FeatureSelect" as C_0013562609546004646591
|
||||
abstract C_0013562609546004646591 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+FeatureSelect(const torch::Tensor & samples, const std::vector<std::string> & features, const std::string & className, const int maxFeatures, const int classNumStates, const torch::Tensor & weights) : void
|
||||
@@ -324,15 +397,15 @@ __
|
||||
#suLabels : std::vector<double>
|
||||
#weights : const torch::Tensor &
|
||||
}
|
||||
class "bayesnet::(anonymous_60342586)" as C_0005584545181746538542
|
||||
class C_0005584545181746538542 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60357672)" as C_0006397015156479549697
|
||||
class C_0006397015156479549697 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+CFS : std::string
|
||||
+FCBF : std::string
|
||||
+IWSS : std::string
|
||||
}
|
||||
class "bayesnet::(anonymous_60343240)" as C_0016227156982041949444
|
||||
class C_0016227156982041949444 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60358326)" as C_0013066254331852347304
|
||||
class C_0013066254331852347304 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+ASC : std::string
|
||||
+DESC : std::string
|
||||
@@ -343,14 +416,17 @@ class C_0009819322948617116148 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+Boost(bool predict_voting = false) : void
|
||||
+~Boost() = default : void
|
||||
..
|
||||
#add_model(std::unique_ptr<Classifier> model, double significance) : void
|
||||
#buildModel(const torch::Tensor & weights) : void
|
||||
#featureSelection(torch::Tensor & weights_) : std::vector<int>
|
||||
#remove_last_model() : void
|
||||
+setHyperparameters(const nlohmann::json & hyperparameters_) : void
|
||||
#update_weights(torch::Tensor & ytrain, torch::Tensor & ypred, torch::Tensor & weights) : std::tuple<torch::Tensor &,double,bool>
|
||||
#update_weights_block(int k, torch::Tensor & ytrain, torch::Tensor & weights) : std::tuple<torch::Tensor &,double,bool>
|
||||
__
|
||||
#X_test : torch::Tensor
|
||||
#X_train : torch::Tensor
|
||||
#alpha_block : bool
|
||||
#bisection : bool
|
||||
#block_update : bool
|
||||
#convergence : bool
|
||||
@@ -364,31 +440,6 @@ __
|
||||
#y_test : torch::Tensor
|
||||
#y_train : torch::Tensor
|
||||
}
|
||||
class "bayesnet::AODELd" as C_0003898187834670349177
|
||||
class C_0003898187834670349177 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+AODELd(bool predict_voting = true) : void
|
||||
+~AODELd() = default : void
|
||||
..
|
||||
#buildModel(const torch::Tensor & weights) : void
|
||||
+fit(torch::Tensor & X_, torch::Tensor & y_, const std::vector<std::string> & features_, const std::string & className_, std::map<std::string,std::vector<int>> & states_, const Smoothing_t smoothing) : AODELd &
|
||||
+graph(const std::string & name = "AODELd") const : std::vector<std::string>
|
||||
#trainModel(const torch::Tensor & weights, const Smoothing_t smoothing) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::(anonymous_60275628)" as C_0009086919615463763584
|
||||
class C_0009086919615463763584 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+CFS : std::string
|
||||
+FCBF : std::string
|
||||
+IWSS : std::string
|
||||
}
|
||||
class "bayesnet::(anonymous_60276282)" as C_0015251985607563196159
|
||||
class C_0015251985607563196159 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+ASC : std::string
|
||||
+DESC : std::string
|
||||
+RAND : std::string
|
||||
}
|
||||
class "bayesnet::BoostA2DE" as C_0000272055465257861326
|
||||
class C_0000272055465257861326 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+BoostA2DE(bool predict_voting = false) : void
|
||||
@@ -398,15 +449,15 @@ class C_0000272055465257861326 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
#trainModel(const torch::Tensor & weights, const Smoothing_t smoothing) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::(anonymous_60275502)" as C_0016033655851510053155
|
||||
class C_0016033655851510053155 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60425028)" as C_0000461144706913711531
|
||||
class C_0000461144706913711531 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+CFS : std::string
|
||||
+FCBF : std::string
|
||||
+IWSS : std::string
|
||||
}
|
||||
class "bayesnet::(anonymous_60276156)" as C_0000379522761622473555
|
||||
class C_0000379522761622473555 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60425682)" as C_0014849589915262463453
|
||||
class C_0014849589915262463453 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+ASC : std::string
|
||||
+DESC : std::string
|
||||
@@ -421,6 +472,38 @@ class C_0002867772739198819061 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
#trainModel(const torch::Tensor & weights, const Smoothing_t smoothing) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::XBA2DE" as C_0008480973840710001141
|
||||
class C_0008480973840710001141 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+XBA2DE(bool predict_voting = false) : void
|
||||
+~XBA2DE() = default : void
|
||||
..
|
||||
+getVersion() : std::string
|
||||
+graph(const std::string & title = "XBA2DE") const : std::vector<std::string>
|
||||
#trainModel(const torch::Tensor & weights, const Smoothing_t smoothing) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::(anonymous_60414016)" as C_0008746994658440620779
|
||||
class C_0008746994658440620779 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+CFS : std::string
|
||||
+FCBF : std::string
|
||||
+IWSS : std::string
|
||||
}
|
||||
class "bayesnet::(anonymous_60414670)" as C_0008030559132212449356
|
||||
class C_0008030559132212449356 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+ASC : std::string
|
||||
+DESC : std::string
|
||||
+RAND : std::string
|
||||
}
|
||||
class "bayesnet::XBAODE" as C_0005198482342493966768
|
||||
class C_0005198482342493966768 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+XBAODE() : void
|
||||
..
|
||||
+getVersion() : std::string
|
||||
#trainModel(const torch::Tensor & weights, const bayesnet::Smoothing_t smoothing) : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::CFS" as C_0000093018845530739957
|
||||
class C_0000093018845530739957 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+CFS(const torch::Tensor & samples, const std::vector<std::string> & features, const std::string & className, const int maxFeatures, const int classNumStates, const torch::Tensor & weights) : void
|
||||
@@ -445,43 +528,43 @@ class C_0000066148117395428429 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
+fit() : void
|
||||
__
|
||||
}
|
||||
class "bayesnet::(anonymous_60730495)" as C_0004857727320042830573
|
||||
class C_0004857727320042830573 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60810808)" as C_0012002108046995621535
|
||||
class C_0012002108046995621535 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+CFS : std::string
|
||||
+FCBF : std::string
|
||||
+IWSS : std::string
|
||||
}
|
||||
class "bayesnet::(anonymous_60731150)" as C_0000076541533312623385
|
||||
class C_0000076541533312623385 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60811462)" as C_0004735044229422764240
|
||||
class C_0004735044229422764240 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+ASC : std::string
|
||||
+DESC : std::string
|
||||
+RAND : std::string
|
||||
}
|
||||
class "bayesnet::(anonymous_60653004)" as C_0001444063444142949758
|
||||
class C_0001444063444142949758 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60804220)" as C_0007082100550474633839
|
||||
class C_0007082100550474633839 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+CFS : std::string
|
||||
+FCBF : std::string
|
||||
+IWSS : std::string
|
||||
}
|
||||
class "bayesnet::(anonymous_60653658)" as C_0007139277546931322856
|
||||
class C_0007139277546931322856 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60804874)" as C_0003669430095936529648
|
||||
class C_0003669430095936529648 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+ASC : std::string
|
||||
+DESC : std::string
|
||||
+RAND : std::string
|
||||
}
|
||||
class "bayesnet::(anonymous_60731375)" as C_0010493853592456211189
|
||||
class C_0010493853592456211189 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60809706)" as C_0012336951062058157227
|
||||
class C_0012336951062058157227 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+CFS : std::string
|
||||
+FCBF : std::string
|
||||
+IWSS : std::string
|
||||
}
|
||||
class "bayesnet::(anonymous_60732030)" as C_0007011438637915849564
|
||||
class C_0007011438637915849564 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
class "bayesnet::(anonymous_60810360)" as C_0002435892998884329673
|
||||
class C_0002435892998884329673 #aliceblue;line:blue;line.dotted;text:blue {
|
||||
__
|
||||
+ASC : std::string
|
||||
+DESC : std::string
|
||||
@@ -513,37 +596,43 @@ C_0010428199432536647474 --> C_0010428199432536647474 : -children
|
||||
C_0009493661199123436603 ..> C_0013393078277439680282
|
||||
C_0009493661199123436603 o-- C_0010428199432536647474 : -nodes
|
||||
C_0002617087915615796317 ..> C_0013393078277439680282
|
||||
C_0002617087915615796317 ..> C_0005907365846270811004
|
||||
C_0002617087915615796317 o-- C_0005907365846270811004 : #status
|
||||
C_0016351972983202413152 ..> C_0013393078277439680282
|
||||
C_0016351972983202413152 ..> C_0005907365846270811004
|
||||
C_0016351972983202413152 o-- C_0009493661199123436603 : #model
|
||||
C_0016351972983202413152 o-- C_0005895723015084986588 : #metrics
|
||||
C_0016351972983202413152 o-- C_0005907365846270811004 : #status
|
||||
C_0002617087915615796317 <|-- C_0016351972983202413152
|
||||
|
||||
C_0017759964713298103839 ..> C_0009493661199123436603
|
||||
C_0016351972983202413152 <|-- C_0008902920152122000044
|
||||
|
||||
C_0016351972983202413152 <|-- C_0004096182510460307610
|
||||
|
||||
C_0016351972983202413152 <|-- C_0016268916386101512883
|
||||
|
||||
C_0016351972983202413152 <|-- C_0014087955399074584137
|
||||
|
||||
C_0017759964713298103839 ..> C_0009493661199123436603
|
||||
C_0002756018222998454702 ..> C_0013393078277439680282
|
||||
C_0008902920152122000044 <|-- C_0002756018222998454702
|
||||
|
||||
C_0017759964713298103839 <|-- C_0002756018222998454702
|
||||
|
||||
C_0016351972983202413152 <|-- C_0004096182510460307610
|
||||
|
||||
C_0010957245114062042836 ..> C_0013393078277439680282
|
||||
C_0004096182510460307610 <|-- C_0010957245114062042836
|
||||
|
||||
C_0017759964713298103839 <|-- C_0010957245114062042836
|
||||
|
||||
C_0016351972983202413152 <|-- C_0016268916386101512883
|
||||
|
||||
C_0016351972983202413152 <|-- C_0014087955399074584137
|
||||
|
||||
C_0013350632773616302678 ..> C_0013393078277439680282
|
||||
C_0014087955399074584137 <|-- C_0013350632773616302678
|
||||
|
||||
C_0017759964713298103839 <|-- C_0013350632773616302678
|
||||
|
||||
C_0007640742442325463418 ..> C_0013393078277439680282
|
||||
C_0016351972983202413152 <|-- C_0007640742442325463418
|
||||
|
||||
C_0015654113248178830206 ..> C_0013393078277439680282
|
||||
C_0016351972983202413152 <|-- C_0015654113248178830206
|
||||
|
||||
C_0015881931090842884611 ..> C_0013393078277439680282
|
||||
C_0015881931090842884611 o-- C_0016351972983202413152 : #models
|
||||
C_0016351972983202413152 <|-- C_0015881931090842884611
|
||||
@@ -552,22 +641,29 @@ C_0015881931090842884611 <|-- C_0001410789567057647859
|
||||
|
||||
C_0015881931090842884611 <|-- C_0006288892608974306258
|
||||
|
||||
C_0005895723015084986588 <|-- C_0013562609546004646591
|
||||
|
||||
C_0009819322948617116148 --> C_0013562609546004646591 : #featureSelector
|
||||
C_0015881931090842884611 <|-- C_0009819322948617116148
|
||||
|
||||
C_0003898187834670349177 ..> C_0013393078277439680282
|
||||
C_0015881931090842884611 <|-- C_0003898187834670349177
|
||||
|
||||
C_0017759964713298103839 <|-- C_0003898187834670349177
|
||||
|
||||
C_0005895723015084986588 <|-- C_0013562609546004646591
|
||||
|
||||
C_0009819322948617116148 ..> C_0016351972983202413152
|
||||
C_0009819322948617116148 --> C_0013562609546004646591 : #featureSelector
|
||||
C_0015881931090842884611 <|-- C_0009819322948617116148
|
||||
|
||||
C_0000272055465257861326 ..> C_0013393078277439680282
|
||||
C_0009819322948617116148 <|-- C_0000272055465257861326
|
||||
|
||||
C_0002867772739198819061 ..> C_0013393078277439680282
|
||||
C_0009819322948617116148 <|-- C_0002867772739198819061
|
||||
|
||||
C_0008480973840710001141 ..> C_0013393078277439680282
|
||||
C_0009819322948617116148 <|-- C_0008480973840710001141
|
||||
|
||||
C_0005198482342493966768 ..> C_0013393078277439680282
|
||||
C_0009819322948617116148 <|-- C_0005198482342493966768
|
||||
|
||||
C_0013562609546004646591 <|-- C_0000093018845530739957
|
||||
|
||||
C_0013562609546004646591 <|-- C_0001157456122733975432
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 229 KiB |
Submodule lib/catch2 deleted from 029fe3b460
Submodule lib/folding deleted from 9652853d69
1
lib/json
1
lib/json
Submodule lib/json deleted from 620034ecec
2009
lib/log/loguru.cpp
2009
lib/log/loguru.cpp
File diff suppressed because it is too large
Load Diff
1475
lib/log/loguru.hpp
1475
lib/log/loguru.hpp
File diff suppressed because it is too large
Load Diff
1
lib/mdlp
1
lib/mdlp
Submodule lib/mdlp deleted from 7d62d6af4a
@@ -1,27 +1,22 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(bayesnet_sample)
|
||||
project(bayesnet_sample VERSION 0.1.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
find_package(Torch REQUIRED)
|
||||
find_library(BayesNet NAMES libBayesNet BayesNet libBayesNet.a REQUIRED)
|
||||
find_path(Bayesnet_INCLUDE_DIRS REQUIRED NAMES bayesnet)
|
||||
find_library(FImdlp NAMES libfimdlp.a PATHS REQUIRED)
|
||||
|
||||
message(STATUS "FImdlp=${FImdlp}")
|
||||
message(STATUS "FImdlp_INCLUDE_DIRS=${FImdlp_INCLUDE_DIRS}")
|
||||
message(STATUS "BayesNet=${BayesNet}")
|
||||
message(STATUS "Bayesnet_INCLUDE_DIRS=${Bayesnet_INCLUDE_DIRS}")
|
||||
|
||||
include_directories(
|
||||
../tests/lib/Files
|
||||
lib/json/include
|
||||
/usr/local/include
|
||||
/usr/local/include/fimdlp/
|
||||
)
|
||||
find_package(Torch CONFIG REQUIRED)
|
||||
find_package(fimdlp CONFIG REQUIRED)
|
||||
find_package(folding CONFIG REQUIRED)
|
||||
find_package(arff-files CONFIG REQUIRED)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
find_package(bayesnet CONFIG REQUIRED)
|
||||
|
||||
add_executable(bayesnet_sample sample.cc)
|
||||
target_link_libraries(bayesnet_sample ${FImdlp} "${TORCH_LIBRARIES}" "${BayesNet}")
|
||||
add_executable(bayesnet_sample_xspode sample_xspode.cc)
|
||||
target_link_libraries(bayesnet_sample_xspode ${FImdlp} "${TORCH_LIBRARIES}" "${BayesNet}")
|
||||
target_link_libraries(bayesnet_sample PRIVATE
|
||||
fimdlp::fimdlp
|
||||
arff-files::arff-files
|
||||
torch::torch
|
||||
bayesnet::bayesnet
|
||||
folding::folding
|
||||
nlohmann_json::nlohmann_json
|
||||
)
|
||||
|
||||
9
sample/CMakeUserPresets.json
Normal file
9
sample/CMakeUserPresets.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": 4,
|
||||
"vendor": {
|
||||
"conan": {}
|
||||
},
|
||||
"include": [
|
||||
"build/CMakePresets.json"
|
||||
]
|
||||
}
|
||||
14
sample/conanfile.txt
Normal file
14
sample/conanfile.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
[requires]
|
||||
libtorch/2.7.0
|
||||
arff-files/1.2.0
|
||||
fimdlp/2.1.0
|
||||
folding/1.1.1
|
||||
bayesnet/1.2.0
|
||||
nlohmann_json/3.11.3
|
||||
|
||||
[generators]
|
||||
CMakeToolchain
|
||||
CMakeDeps
|
||||
|
||||
[options]
|
||||
libtorch/2.7.0:shared=True
|
||||
@@ -1,55 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/conversions/from_json.hpp>
|
||||
#include <nlohmann/detail/conversions/to_json.hpp>
|
||||
#include <nlohmann/detail/meta/identity_tag.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/adl_serializer/
|
||||
template<typename ValueType, typename>
|
||||
struct adl_serializer
|
||||
{
|
||||
/// @brief convert a JSON value to any value type
|
||||
/// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
|
||||
template<typename BasicJsonType, typename TargetType = ValueType>
|
||||
static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
|
||||
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
|
||||
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
|
||||
{
|
||||
::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
|
||||
}
|
||||
|
||||
/// @brief convert a JSON value to any value type
|
||||
/// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
|
||||
template<typename BasicJsonType, typename TargetType = ValueType>
|
||||
static auto from_json(BasicJsonType && j) noexcept(
|
||||
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
|
||||
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
|
||||
{
|
||||
return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
|
||||
}
|
||||
|
||||
/// @brief convert any value type to a JSON value
|
||||
/// @sa https://json.nlohmann.me/api/adl_serializer/to_json/
|
||||
template<typename BasicJsonType, typename TargetType = ValueType>
|
||||
static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
|
||||
noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))
|
||||
-> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
|
||||
{
|
||||
::nlohmann::to_json(j, std::forward<TargetType>(val));
|
||||
}
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,103 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // uint8_t, uint64_t
|
||||
#include <tuple> // tie
|
||||
#include <utility> // move
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/// @brief an internal type for a backed binary type
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/
|
||||
template<typename BinaryType>
|
||||
class byte_container_with_subtype : public BinaryType
|
||||
{
|
||||
public:
|
||||
using container_type = BinaryType;
|
||||
using subtype_type = std::uint64_t;
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
||||
byte_container_with_subtype() noexcept(noexcept(container_type()))
|
||||
: container_type()
|
||||
{}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
||||
byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))
|
||||
: container_type(b)
|
||||
{}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
||||
byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))
|
||||
: container_type(std::move(b))
|
||||
{}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
||||
byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))
|
||||
: container_type(b)
|
||||
, m_subtype(subtype_)
|
||||
, m_has_subtype(true)
|
||||
{}
|
||||
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
||||
byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))
|
||||
: container_type(std::move(b))
|
||||
, m_subtype(subtype_)
|
||||
, m_has_subtype(true)
|
||||
{}
|
||||
|
||||
bool operator==(const byte_container_with_subtype& rhs) const
|
||||
{
|
||||
return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
|
||||
std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
|
||||
}
|
||||
|
||||
bool operator!=(const byte_container_with_subtype& rhs) const
|
||||
{
|
||||
return !(rhs == *this);
|
||||
}
|
||||
|
||||
/// @brief sets the binary subtype
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/
|
||||
void set_subtype(subtype_type subtype_) noexcept
|
||||
{
|
||||
m_subtype = subtype_;
|
||||
m_has_subtype = true;
|
||||
}
|
||||
|
||||
/// @brief return the binary subtype
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/
|
||||
constexpr subtype_type subtype() const noexcept
|
||||
{
|
||||
return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1);
|
||||
}
|
||||
|
||||
/// @brief return whether the value has a subtype
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/
|
||||
constexpr bool has_subtype() const noexcept
|
||||
{
|
||||
return m_has_subtype;
|
||||
}
|
||||
|
||||
/// @brief clears the binary subtype
|
||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/
|
||||
void clear_subtype() noexcept
|
||||
{
|
||||
m_subtype = 0;
|
||||
m_has_subtype = false;
|
||||
}
|
||||
|
||||
private:
|
||||
subtype_type m_subtype = 0;
|
||||
bool m_has_subtype = false;
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,100 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
// This file contains all macro definitions affecting or depending on the ABI
|
||||
|
||||
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
|
||||
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
|
||||
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3
|
||||
#warning "Already included a different version of the library!"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum)
|
||||
|
||||
#ifndef JSON_DIAGNOSTICS
|
||||
#define JSON_DIAGNOSTICS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
|
||||
#endif
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
_v ## major ## _ ## minor ## _ ## patch
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
|
||||
|
||||
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION
|
||||
#else
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
|
||||
NLOHMANN_JSON_VERSION_MINOR, \
|
||||
NLOHMANN_JSON_VERSION_PATCH)
|
||||
#endif
|
||||
|
||||
// Combine namespace components
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
|
||||
NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE
|
||||
#define NLOHMANN_JSON_NAMESPACE \
|
||||
nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAGS, \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION)
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
#define NLOHMANN_JSON_NAMESPACE_BEGIN \
|
||||
namespace nlohmann \
|
||||
{ \
|
||||
inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAGS, \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION) \
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_END
|
||||
#define NLOHMANN_JSON_NAMESPACE_END \
|
||||
} /* namespace (inline namespace) NOLINT(readability/namespace) */ \
|
||||
} // namespace nlohmann
|
||||
#endif
|
||||
@@ -1,497 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // transform
|
||||
#include <array> // array
|
||||
#include <forward_list> // forward_list
|
||||
#include <iterator> // inserter, front_inserter, end
|
||||
#include <map> // map
|
||||
#include <string> // string
|
||||
#include <tuple> // tuple, make_tuple
|
||||
#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
|
||||
#include <unordered_map> // unordered_map
|
||||
#include <utility> // pair, declval
|
||||
#include <valarray> // valarray
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/meta/identity_tag.hpp>
|
||||
#include <nlohmann/detail/meta/std_fs.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
|
||||
}
|
||||
n = nullptr;
|
||||
}
|
||||
|
||||
// overloads for basic_json template parameters
|
||||
template < typename BasicJsonType, typename ArithmeticType,
|
||||
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
|
||||
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
||||
int > = 0 >
|
||||
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
||||
{
|
||||
switch (static_cast<value_t>(j))
|
||||
{
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
||||
break;
|
||||
}
|
||||
case value_t::number_integer:
|
||||
{
|
||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
|
||||
break;
|
||||
}
|
||||
case value_t::number_float:
|
||||
{
|
||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
|
||||
}
|
||||
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
|
||||
}
|
||||
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
||||
}
|
||||
|
||||
template <
|
||||
typename BasicJsonType, typename StringType,
|
||||
enable_if_t <
|
||||
std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
|
||||
&& is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value
|
||||
&& !std::is_same<typename BasicJsonType::string_t, StringType>::value
|
||||
&& !is_json_ref<StringType>::value, int > = 0 >
|
||||
inline void from_json(const BasicJsonType& j, StringType& s)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
|
||||
}
|
||||
|
||||
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
|
||||
{
|
||||
get_arithmetic_value(j, val);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
|
||||
{
|
||||
get_arithmetic_value(j, val);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
|
||||
{
|
||||
get_arithmetic_value(j, val);
|
||||
}
|
||||
|
||||
#if !JSON_DISABLE_ENUM_SERIALIZATION
|
||||
template<typename BasicJsonType, typename EnumType,
|
||||
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
||||
inline void from_json(const BasicJsonType& j, EnumType& e)
|
||||
{
|
||||
typename std::underlying_type<EnumType>::type val;
|
||||
get_arithmetic_value(j, val);
|
||||
e = static_cast<EnumType>(val);
|
||||
}
|
||||
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
||||
|
||||
// forward_list doesn't have an insert method
|
||||
template<typename BasicJsonType, typename T, typename Allocator,
|
||||
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
|
||||
inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||
}
|
||||
l.clear();
|
||||
std::transform(j.rbegin(), j.rend(),
|
||||
std::front_inserter(l), [](const BasicJsonType & i)
|
||||
{
|
||||
return i.template get<T>();
|
||||
});
|
||||
}
|
||||
|
||||
// valarray doesn't have an insert method
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
|
||||
inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||
}
|
||||
l.resize(j.size());
|
||||
std::transform(j.begin(), j.end(), std::begin(l),
|
||||
[](const BasicJsonType & elem)
|
||||
{
|
||||
return elem.template get<T>();
|
||||
});
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T, std::size_t N>
|
||||
auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
-> decltype(j.template get<T>(), void())
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
arr[i] = j.at(i).template get<T>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
|
||||
{
|
||||
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T, std::size_t N>
|
||||
auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
|
||||
priority_tag<2> /*unused*/)
|
||||
-> decltype(j.template get<T>(), void())
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
arr[i] = j.at(i).template get<T>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleArrayType,
|
||||
enable_if_t<
|
||||
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
|
||||
int> = 0>
|
||||
auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
|
||||
-> decltype(
|
||||
arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
|
||||
j.template get<typename ConstructibleArrayType::value_type>(),
|
||||
void())
|
||||
{
|
||||
using std::end;
|
||||
|
||||
ConstructibleArrayType ret;
|
||||
ret.reserve(j.size());
|
||||
std::transform(j.begin(), j.end(),
|
||||
std::inserter(ret, end(ret)), [](const BasicJsonType & i)
|
||||
{
|
||||
// get<BasicJsonType>() returns *this, this won't call a from_json
|
||||
// method when value_type is BasicJsonType
|
||||
return i.template get<typename ConstructibleArrayType::value_type>();
|
||||
});
|
||||
arr = std::move(ret);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleArrayType,
|
||||
enable_if_t<
|
||||
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
|
||||
int> = 0>
|
||||
inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
|
||||
priority_tag<0> /*unused*/)
|
||||
{
|
||||
using std::end;
|
||||
|
||||
ConstructibleArrayType ret;
|
||||
std::transform(
|
||||
j.begin(), j.end(), std::inserter(ret, end(ret)),
|
||||
[](const BasicJsonType & i)
|
||||
{
|
||||
// get<BasicJsonType>() returns *this, this won't call a from_json
|
||||
// method when value_type is BasicJsonType
|
||||
return i.template get<typename ConstructibleArrayType::value_type>();
|
||||
});
|
||||
arr = std::move(ret);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename ConstructibleArrayType,
|
||||
enable_if_t <
|
||||
is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
|
||||
!is_basic_json<ConstructibleArrayType>::value,
|
||||
int > = 0 >
|
||||
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
|
||||
-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
|
||||
j.template get<typename ConstructibleArrayType::value_type>(),
|
||||
void())
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||
}
|
||||
|
||||
from_json_array_impl(j, arr, priority_tag<3> {});
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename T, std::size_t... Idx >
|
||||
std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
|
||||
identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
|
||||
{
|
||||
return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename T, std::size_t N >
|
||||
auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
|
||||
-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||
}
|
||||
|
||||
return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
|
||||
}
|
||||
|
||||
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
||||
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
|
||||
}
|
||||
|
||||
ConstructibleObjectType ret;
|
||||
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
||||
using value_type = typename ConstructibleObjectType::value_type;
|
||||
std::transform(
|
||||
inner_object->begin(), inner_object->end(),
|
||||
std::inserter(ret, ret.begin()),
|
||||
[](typename BasicJsonType::object_t::value_type const & p)
|
||||
{
|
||||
return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
||||
});
|
||||
obj = std::move(ret);
|
||||
}
|
||||
|
||||
// overload for arithmetic types, not chosen for basic_json template arguments
|
||||
// (BooleanType, etc..); note: Is it really necessary to provide explicit
|
||||
// overloads for boolean_t etc. in case of a custom BooleanType which is not
|
||||
// an arithmetic type?
|
||||
template < typename BasicJsonType, typename ArithmeticType,
|
||||
enable_if_t <
|
||||
std::is_arithmetic<ArithmeticType>::value&&
|
||||
!std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
|
||||
!std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
|
||||
!std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
|
||||
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
||||
int > = 0 >
|
||||
inline void from_json(const BasicJsonType& j, ArithmeticType& val)
|
||||
{
|
||||
switch (static_cast<value_t>(j))
|
||||
{
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
||||
break;
|
||||
}
|
||||
case value_t::number_integer:
|
||||
{
|
||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
|
||||
break;
|
||||
}
|
||||
case value_t::number_float:
|
||||
{
|
||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
|
||||
break;
|
||||
}
|
||||
case value_t::boolean:
|
||||
{
|
||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::object:
|
||||
case value_t::array:
|
||||
case value_t::string:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename... Args, std::size_t... Idx>
|
||||
std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
|
||||
{
|
||||
return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, class A1, class A2 >
|
||||
std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
|
||||
{
|
||||
return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
|
||||
std::forward<BasicJsonType>(j).at(1).template get<A2>()};
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename A1, typename A2>
|
||||
inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
|
||||
{
|
||||
p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename... Args>
|
||||
std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
|
||||
{
|
||||
return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename... Args>
|
||||
inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
|
||||
{
|
||||
t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename TupleRelated>
|
||||
auto from_json(BasicJsonType&& j, TupleRelated&& t)
|
||||
-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||
}
|
||||
|
||||
return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
|
||||
typename = enable_if_t < !std::is_constructible <
|
||||
typename BasicJsonType::string_t, Key >::value >>
|
||||
inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||
}
|
||||
m.clear();
|
||||
for (const auto& p : j)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
|
||||
}
|
||||
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
|
||||
typename = enable_if_t < !std::is_constructible <
|
||||
typename BasicJsonType::string_t, Key >::value >>
|
||||
inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
||||
}
|
||||
m.clear();
|
||||
for (const auto& p : j)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
|
||||
}
|
||||
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
||||
}
|
||||
}
|
||||
|
||||
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
template<typename BasicJsonType>
|
||||
inline void from_json(const BasicJsonType& j, std_fs::path& p)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
|
||||
}
|
||||
p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
||||
}
|
||||
#endif
|
||||
|
||||
struct from_json_fn
|
||||
{
|
||||
template<typename BasicJsonType, typename T>
|
||||
auto operator()(const BasicJsonType& j, T&& val) const
|
||||
noexcept(noexcept(from_json(j, std::forward<T>(val))))
|
||||
-> decltype(from_json(j, std::forward<T>(val)))
|
||||
{
|
||||
return from_json(j, std::forward<T>(val));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#ifndef JSON_HAS_CPP_17
|
||||
/// namespace to hold default `from_json` function
|
||||
/// to see why this is required:
|
||||
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
||||
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
|
||||
{
|
||||
#endif
|
||||
JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
|
||||
detail::static_const<detail::from_json_fn>::value;
|
||||
#ifndef JSON_HAS_CPP_17
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,447 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // copy
|
||||
#include <iterator> // begin, end
|
||||
#include <string> // string
|
||||
#include <tuple> // tuple, get
|
||||
#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
|
||||
#include <utility> // move, forward, declval, pair
|
||||
#include <valarray> // valarray
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/meta/std_fs.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
//////////////////
|
||||
// constructors //
|
||||
//////////////////
|
||||
|
||||
/*
|
||||
* Note all external_constructor<>::construct functions need to call
|
||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
||||
* allocated value (e.g., a string). See bug issue
|
||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||
*/
|
||||
|
||||
template<value_t> struct external_constructor;
|
||||
|
||||
template<>
|
||||
struct external_constructor<value_t::boolean>
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::boolean;
|
||||
j.m_data.m_value = b;
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct external_constructor<value_t::string>
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = s;
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = std::move(s);
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleStringType,
|
||||
enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
|
||||
int > = 0 >
|
||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct external_constructor<value_t::binary>
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct external_constructor<value_t::number_float>
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::number_float;
|
||||
j.m_data.m_value = val;
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct external_constructor<value_t::number_unsigned>
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::number_unsigned;
|
||||
j.m_data.m_value = val;
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct external_constructor<value_t::number_integer>
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::number_integer;
|
||||
j.m_data.m_value = val;
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct external_constructor<value_t::array>
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = arr;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = std::move(arr);
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
|
||||
int > = 0 >
|
||||
static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->reserve(arr.size());
|
||||
for (const bool x : arr)
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
}
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->resize(arr.size());
|
||||
if (arr.size() > 0)
|
||||
{
|
||||
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
||||
}
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct external_constructor<value_t::object>
|
||||
{
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = obj;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = std::move(obj);
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleObjectType,
|
||||
enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
|
||||
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
|
||||
/////////////
|
||||
// to_json //
|
||||
/////////////
|
||||
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, T b) noexcept
|
||||
{
|
||||
external_constructor<value_t::boolean>::construct(j, b);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename BoolRef,
|
||||
enable_if_t <
|
||||
((std::is_same<std::vector<bool>::reference, BoolRef>::value
|
||||
&& !std::is_same <std::vector<bool>::reference, typename BasicJsonType::boolean_t&>::value)
|
||||
|| (std::is_same<std::vector<bool>::const_reference, BoolRef>::value
|
||||
&& !std::is_same <detail::uncvref_t<std::vector<bool>::const_reference>,
|
||||
typename BasicJsonType::boolean_t >::value))
|
||||
&& std::is_convertible<const BoolRef&, typename BasicJsonType::boolean_t>::value, int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const BoolRef& b) noexcept
|
||||
{
|
||||
external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleString,
|
||||
enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, const CompatibleString& s)
|
||||
{
|
||||
external_constructor<value_t::string>::construct(j, s);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||
{
|
||||
external_constructor<value_t::string>::construct(j, std::move(s));
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename FloatType,
|
||||
enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, FloatType val) noexcept
|
||||
{
|
||||
external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
|
||||
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
|
||||
{
|
||||
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
|
||||
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
|
||||
{
|
||||
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
|
||||
}
|
||||
|
||||
#if !JSON_DISABLE_ENUM_SERIALIZATION
|
||||
template<typename BasicJsonType, typename EnumType,
|
||||
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, EnumType e) noexcept
|
||||
{
|
||||
using underlying_type = typename std::underlying_type<EnumType>::type;
|
||||
static constexpr value_t integral_value_t = std::is_unsigned<underlying_type>::value ? value_t::number_unsigned : value_t::number_integer;
|
||||
external_constructor<integral_value_t>::construct(j, static_cast<underlying_type>(e));
|
||||
}
|
||||
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
|
||||
{
|
||||
external_constructor<value_t::array>::construct(j, e);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t < is_compatible_array_type<BasicJsonType,
|
||||
CompatibleArrayType>::value&&
|
||||
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
||||
!is_basic_json<CompatibleArrayType>::value,
|
||||
int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
|
||||
{
|
||||
external_constructor<value_t::array>::construct(j, arr);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
|
||||
{
|
||||
external_constructor<value_t::binary>::construct(j, bin);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
|
||||
{
|
||||
external_constructor<value_t::array>::construct(j, std::move(arr));
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||
{
|
||||
external_constructor<value_t::array>::construct(j, std::move(arr));
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleObjectType,
|
||||
enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
|
||||
{
|
||||
external_constructor<value_t::object>::construct(j, obj);
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||
{
|
||||
external_constructor<value_t::object>::construct(j, std::move(obj));
|
||||
}
|
||||
|
||||
template <
|
||||
typename BasicJsonType, typename T, std::size_t N,
|
||||
enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
|
||||
const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
{
|
||||
external_constructor<value_t::array>::construct(j, arr);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
|
||||
{
|
||||
j = { p.first, p.second };
|
||||
}
|
||||
|
||||
// for https://github.com/nlohmann/json/pull/1134
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, const T& b)
|
||||
{
|
||||
j = { {b.key(), b.value()} };
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
|
||||
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
|
||||
{
|
||||
j = { std::get<Idx>(t)... };
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
|
||||
inline void to_json(BasicJsonType& j, const T& t)
|
||||
{
|
||||
to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
|
||||
}
|
||||
|
||||
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, const std_fs::path& p)
|
||||
{
|
||||
j = p.string();
|
||||
}
|
||||
#endif
|
||||
|
||||
struct to_json_fn
|
||||
{
|
||||
template<typename BasicJsonType, typename T>
|
||||
auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
|
||||
-> decltype(to_json(j, std::forward<T>(val)), void())
|
||||
{
|
||||
return to_json(j, std::forward<T>(val));
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
#ifndef JSON_HAS_CPP_17
|
||||
/// namespace to hold default `to_json` function
|
||||
/// to see why this is required:
|
||||
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
||||
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
|
||||
{
|
||||
#endif
|
||||
JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
|
||||
detail::static_const<detail::to_json_fn>::value;
|
||||
#ifndef JSON_HAS_CPP_17
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,257 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // nullptr_t
|
||||
#include <exception> // exception
|
||||
#if JSON_DIAGNOSTICS
|
||||
#include <numeric> // accumulate
|
||||
#endif
|
||||
#include <stdexcept> // runtime_error
|
||||
#include <string> // to_string
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
#include <nlohmann/detail/string_escape.hpp>
|
||||
#include <nlohmann/detail/input/position_t.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
////////////////
|
||||
// exceptions //
|
||||
////////////////
|
||||
|
||||
/// @brief general exception of the @ref basic_json class
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/exception/
|
||||
class exception : public std::exception
|
||||
{
|
||||
public:
|
||||
/// returns the explanatory string
|
||||
const char* what() const noexcept override
|
||||
{
|
||||
return m.what();
|
||||
}
|
||||
|
||||
/// the id of the exception
|
||||
const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
|
||||
|
||||
protected:
|
||||
JSON_HEDLEY_NON_NULL(3)
|
||||
exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
|
||||
|
||||
static std::string name(const std::string& ename, int id_)
|
||||
{
|
||||
return concat("[json.exception.", ename, '.', std::to_string(id_), "] ");
|
||||
}
|
||||
|
||||
static std::string diagnostics(std::nullptr_t /*leaf_element*/)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static std::string diagnostics(const BasicJsonType* leaf_element)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
std::vector<std::string> tokens;
|
||||
for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)
|
||||
{
|
||||
switch (current->m_parent->type())
|
||||
{
|
||||
case value_t::array:
|
||||
{
|
||||
for (std::size_t i = 0; i < current->m_parent->m_data.m_value.array->size(); ++i)
|
||||
{
|
||||
if (¤t->m_parent->m_data.m_value.array->operator[](i) == current)
|
||||
{
|
||||
tokens.emplace_back(std::to_string(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::object:
|
||||
{
|
||||
for (const auto& element : *current->m_parent->m_data.m_value.object)
|
||||
{
|
||||
if (&element.second == current)
|
||||
{
|
||||
tokens.emplace_back(element.first.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null: // LCOV_EXCL_LINE
|
||||
case value_t::string: // LCOV_EXCL_LINE
|
||||
case value_t::boolean: // LCOV_EXCL_LINE
|
||||
case value_t::number_integer: // LCOV_EXCL_LINE
|
||||
case value_t::number_unsigned: // LCOV_EXCL_LINE
|
||||
case value_t::number_float: // LCOV_EXCL_LINE
|
||||
case value_t::binary: // LCOV_EXCL_LINE
|
||||
case value_t::discarded: // LCOV_EXCL_LINE
|
||||
default: // LCOV_EXCL_LINE
|
||||
break; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens.empty())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
||||
[](const std::string & a, const std::string & b)
|
||||
{
|
||||
return concat(a, '/', detail::escape(b));
|
||||
});
|
||||
return concat('(', str, ") ");
|
||||
#else
|
||||
static_cast<void>(leaf_element);
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
/// an exception object as storage for error messages
|
||||
std::runtime_error m;
|
||||
};
|
||||
|
||||
/// @brief exception indicating a parse error
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/parse_error/
|
||||
class parse_error : public exception
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
@brief create a parse error exception
|
||||
@param[in] id_ the id of the exception
|
||||
@param[in] pos the position where the error occurred (or with
|
||||
chars_read_total=0 if the position cannot be
|
||||
determined)
|
||||
@param[in] what_arg the explanatory string
|
||||
@return parse_error object
|
||||
*/
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = concat(exception::name("parse_error", id_), "parse error",
|
||||
position_string(pos), ": ", exception::diagnostics(context), what_arg);
|
||||
return {id_, pos.chars_read_total, w.c_str()};
|
||||
}
|
||||
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = concat(exception::name("parse_error", id_), "parse error",
|
||||
(byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""),
|
||||
": ", exception::diagnostics(context), what_arg);
|
||||
return {id_, byte_, w.c_str()};
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief byte index of the parse error
|
||||
|
||||
The byte index of the last read character in the input file.
|
||||
|
||||
@note For an input with n bytes, 1 is the index of the first character and
|
||||
n+1 is the index of the terminating null byte or the end of file.
|
||||
This also holds true when reading a byte vector (CBOR or MessagePack).
|
||||
*/
|
||||
const std::size_t byte;
|
||||
|
||||
private:
|
||||
parse_error(int id_, std::size_t byte_, const char* what_arg)
|
||||
: exception(id_, what_arg), byte(byte_) {}
|
||||
|
||||
static std::string position_string(const position_t& pos)
|
||||
{
|
||||
return concat(" at line ", std::to_string(pos.lines_read + 1),
|
||||
", column ", std::to_string(pos.chars_read_current_line));
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief exception indicating errors with iterators
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/
|
||||
class invalid_iterator : public exception
|
||||
{
|
||||
public:
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg);
|
||||
return {id_, w.c_str()};
|
||||
}
|
||||
|
||||
private:
|
||||
JSON_HEDLEY_NON_NULL(3)
|
||||
invalid_iterator(int id_, const char* what_arg)
|
||||
: exception(id_, what_arg) {}
|
||||
};
|
||||
|
||||
/// @brief exception indicating executing a member function with a wrong type
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/type_error/
|
||||
class type_error : public exception
|
||||
{
|
||||
public:
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg);
|
||||
return {id_, w.c_str()};
|
||||
}
|
||||
|
||||
private:
|
||||
JSON_HEDLEY_NON_NULL(3)
|
||||
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||
};
|
||||
|
||||
/// @brief exception indicating access out of the defined range
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/
|
||||
class out_of_range : public exception
|
||||
{
|
||||
public:
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg);
|
||||
return {id_, w.c_str()};
|
||||
}
|
||||
|
||||
private:
|
||||
JSON_HEDLEY_NON_NULL(3)
|
||||
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||
};
|
||||
|
||||
/// @brief exception indicating other library errors
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/other_error/
|
||||
class other_error : public exception
|
||||
{
|
||||
public:
|
||||
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
||||
static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
|
||||
{
|
||||
const std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg);
|
||||
return {id_, w.c_str()};
|
||||
}
|
||||
|
||||
private:
|
||||
JSON_HEDLEY_NON_NULL(3)
|
||||
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,129 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // uint8_t
|
||||
#include <cstddef> // size_t
|
||||
#include <functional> // hash
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// boost::hash_combine
|
||||
inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
|
||||
{
|
||||
seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);
|
||||
return seed;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief hash a JSON value
|
||||
|
||||
The hash function tries to rely on std::hash where possible. Furthermore, the
|
||||
type of the JSON value is taken into account to have different hash values for
|
||||
null, 0, 0U, and false, etc.
|
||||
|
||||
@tparam BasicJsonType basic_json specialization
|
||||
@param j JSON value to hash
|
||||
@return hash value of j
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
std::size_t hash(const BasicJsonType& j)
|
||||
{
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
|
||||
const auto type = static_cast<std::size_t>(j.type());
|
||||
switch (j.type())
|
||||
{
|
||||
case BasicJsonType::value_t::null:
|
||||
case BasicJsonType::value_t::discarded:
|
||||
{
|
||||
return combine(type, 0);
|
||||
}
|
||||
|
||||
case BasicJsonType::value_t::object:
|
||||
{
|
||||
auto seed = combine(type, j.size());
|
||||
for (const auto& element : j.items())
|
||||
{
|
||||
const auto h = std::hash<string_t> {}(element.key());
|
||||
seed = combine(seed, h);
|
||||
seed = combine(seed, hash(element.value()));
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
|
||||
case BasicJsonType::value_t::array:
|
||||
{
|
||||
auto seed = combine(type, j.size());
|
||||
for (const auto& element : j)
|
||||
{
|
||||
seed = combine(seed, hash(element));
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
|
||||
case BasicJsonType::value_t::string:
|
||||
{
|
||||
const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());
|
||||
return combine(type, h);
|
||||
}
|
||||
|
||||
case BasicJsonType::value_t::boolean:
|
||||
{
|
||||
const auto h = std::hash<bool> {}(j.template get<bool>());
|
||||
return combine(type, h);
|
||||
}
|
||||
|
||||
case BasicJsonType::value_t::number_integer:
|
||||
{
|
||||
const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
|
||||
return combine(type, h);
|
||||
}
|
||||
|
||||
case BasicJsonType::value_t::number_unsigned:
|
||||
{
|
||||
const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
|
||||
return combine(type, h);
|
||||
}
|
||||
|
||||
case BasicJsonType::value_t::number_float:
|
||||
{
|
||||
const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
|
||||
return combine(type, h);
|
||||
}
|
||||
|
||||
case BasicJsonType::value_t::binary:
|
||||
{
|
||||
auto seed = combine(type, j.get_binary().size());
|
||||
const auto h = std::hash<bool> {}(j.get_binary().has_subtype());
|
||||
seed = combine(seed, h);
|
||||
seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));
|
||||
for (const auto byte : j.get_binary())
|
||||
{
|
||||
seed = combine(seed, std::hash<std::uint8_t> {}(byte));
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
return 0; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,492 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstring> // strlen
|
||||
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
|
||||
#include <memory> // shared_ptr, make_shared, addressof
|
||||
#include <numeric> // accumulate
|
||||
#include <string> // string, char_traits
|
||||
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
||||
#include <utility> // pair, declval
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
#include <cstdio> // FILE *
|
||||
#include <istream> // istream
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
#include <nlohmann/detail/iterators/iterator_traits.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// the supported input formats
|
||||
enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
|
||||
|
||||
////////////////////
|
||||
// input adapters //
|
||||
////////////////////
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
/*!
|
||||
Input adapter for stdio file access. This adapter read only 1 byte and do not use any
|
||||
buffer. This adapter is a very low level adapter.
|
||||
*/
|
||||
class file_input_adapter
|
||||
{
|
||||
public:
|
||||
using char_type = char;
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
explicit file_input_adapter(std::FILE* f) noexcept
|
||||
: m_file(f)
|
||||
{
|
||||
JSON_ASSERT(m_file != nullptr);
|
||||
}
|
||||
|
||||
// make class move-only
|
||||
file_input_adapter(const file_input_adapter&) = delete;
|
||||
file_input_adapter(file_input_adapter&&) noexcept = default;
|
||||
file_input_adapter& operator=(const file_input_adapter&) = delete;
|
||||
file_input_adapter& operator=(file_input_adapter&&) = delete;
|
||||
~file_input_adapter() = default;
|
||||
|
||||
std::char_traits<char>::int_type get_character() noexcept
|
||||
{
|
||||
return std::fgetc(m_file);
|
||||
}
|
||||
|
||||
private:
|
||||
/// the file pointer to read from
|
||||
std::FILE* m_file;
|
||||
};
|
||||
|
||||
/*!
|
||||
Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
|
||||
beginning of input. Does not support changing the underlying std::streambuf
|
||||
in mid-input. Maintains underlying std::istream and std::streambuf to support
|
||||
subsequent use of standard std::istream operations to process any input
|
||||
characters following those used in parsing the JSON input. Clears the
|
||||
std::istream flags; any input errors (e.g., EOF) will be detected by the first
|
||||
subsequent call for input from the std::istream.
|
||||
*/
|
||||
class input_stream_adapter
|
||||
{
|
||||
public:
|
||||
using char_type = char;
|
||||
|
||||
~input_stream_adapter()
|
||||
{
|
||||
// clear stream flags; we use underlying streambuf I/O, do not
|
||||
// maintain ifstream flags, except eof
|
||||
if (is != nullptr)
|
||||
{
|
||||
is->clear(is->rdstate() & std::ios::eofbit);
|
||||
}
|
||||
}
|
||||
|
||||
explicit input_stream_adapter(std::istream& i)
|
||||
: is(&i), sb(i.rdbuf())
|
||||
{}
|
||||
|
||||
// delete because of pointer members
|
||||
input_stream_adapter(const input_stream_adapter&) = delete;
|
||||
input_stream_adapter& operator=(input_stream_adapter&) = delete;
|
||||
input_stream_adapter& operator=(input_stream_adapter&&) = delete;
|
||||
|
||||
input_stream_adapter(input_stream_adapter&& rhs) noexcept
|
||||
: is(rhs.is), sb(rhs.sb)
|
||||
{
|
||||
rhs.is = nullptr;
|
||||
rhs.sb = nullptr;
|
||||
}
|
||||
|
||||
// std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
|
||||
// ensure that std::char_traits<char>::eof() and the character 0xFF do not
|
||||
// end up as the same value, e.g. 0xFFFFFFFF.
|
||||
std::char_traits<char>::int_type get_character()
|
||||
{
|
||||
auto res = sb->sbumpc();
|
||||
// set eof manually, as we don't use the istream interface.
|
||||
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
||||
{
|
||||
is->clear(is->rdstate() | std::ios::eofbit);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
/// the associated input stream
|
||||
std::istream* is = nullptr;
|
||||
std::streambuf* sb = nullptr;
|
||||
};
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
// General-purpose iterator-based adapter. It might not be as fast as
|
||||
// theoretically possible for some containers, but it is extremely versatile.
|
||||
template<typename IteratorType>
|
||||
class iterator_input_adapter
|
||||
{
|
||||
public:
|
||||
using char_type = typename std::iterator_traits<IteratorType>::value_type;
|
||||
|
||||
iterator_input_adapter(IteratorType first, IteratorType last)
|
||||
: current(std::move(first)), end(std::move(last))
|
||||
{}
|
||||
|
||||
typename char_traits<char_type>::int_type get_character()
|
||||
{
|
||||
if (JSON_HEDLEY_LIKELY(current != end))
|
||||
{
|
||||
auto result = char_traits<char_type>::to_int_type(*current);
|
||||
std::advance(current, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
return char_traits<char_type>::eof();
|
||||
}
|
||||
|
||||
private:
|
||||
IteratorType current;
|
||||
IteratorType end;
|
||||
|
||||
template<typename BaseInputAdapter, size_t T>
|
||||
friend struct wide_string_input_helper;
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return current == end;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename BaseInputAdapter, size_t T>
|
||||
struct wide_string_input_helper;
|
||||
|
||||
template<typename BaseInputAdapter>
|
||||
struct wide_string_input_helper<BaseInputAdapter, 4>
|
||||
{
|
||||
// UTF-32
|
||||
static void fill_buffer(BaseInputAdapter& input,
|
||||
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
|
||||
size_t& utf8_bytes_index,
|
||||
size_t& utf8_bytes_filled)
|
||||
{
|
||||
utf8_bytes_index = 0;
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(input.empty()))
|
||||
{
|
||||
utf8_bytes[0] = std::char_traits<char>::eof();
|
||||
utf8_bytes_filled = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// get the current character
|
||||
const auto wc = input.get_character();
|
||||
|
||||
// UTF-32 to UTF-8 encoding
|
||||
if (wc < 0x80)
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||
utf8_bytes_filled = 1;
|
||||
}
|
||||
else if (wc <= 0x7FF)
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
|
||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
||||
utf8_bytes_filled = 2;
|
||||
}
|
||||
else if (wc <= 0xFFFF)
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
|
||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
|
||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
||||
utf8_bytes_filled = 3;
|
||||
}
|
||||
else if (wc <= 0x10FFFF)
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
|
||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
|
||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
|
||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
||||
utf8_bytes_filled = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown character
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||
utf8_bytes_filled = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename BaseInputAdapter>
|
||||
struct wide_string_input_helper<BaseInputAdapter, 2>
|
||||
{
|
||||
// UTF-16
|
||||
static void fill_buffer(BaseInputAdapter& input,
|
||||
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
|
||||
size_t& utf8_bytes_index,
|
||||
size_t& utf8_bytes_filled)
|
||||
{
|
||||
utf8_bytes_index = 0;
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(input.empty()))
|
||||
{
|
||||
utf8_bytes[0] = std::char_traits<char>::eof();
|
||||
utf8_bytes_filled = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// get the current character
|
||||
const auto wc = input.get_character();
|
||||
|
||||
// UTF-16 to UTF-8 encoding
|
||||
if (wc < 0x80)
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||
utf8_bytes_filled = 1;
|
||||
}
|
||||
else if (wc <= 0x7FF)
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
|
||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
||||
utf8_bytes_filled = 2;
|
||||
}
|
||||
else if (0xD800 > wc || wc >= 0xE000)
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
|
||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
|
||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
||||
utf8_bytes_filled = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
|
||||
{
|
||||
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
||||
utf8_bytes_filled = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||
utf8_bytes_filled = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Wraps another input adapter to convert wide character types into individual bytes.
|
||||
template<typename BaseInputAdapter, typename WideCharType>
|
||||
class wide_string_input_adapter
|
||||
{
|
||||
public:
|
||||
using char_type = char;
|
||||
|
||||
wide_string_input_adapter(BaseInputAdapter base)
|
||||
: base_adapter(base) {}
|
||||
|
||||
typename std::char_traits<char>::int_type get_character() noexcept
|
||||
{
|
||||
// check if buffer needs to be filled
|
||||
if (utf8_bytes_index == utf8_bytes_filled)
|
||||
{
|
||||
fill_buffer<sizeof(WideCharType)>();
|
||||
|
||||
JSON_ASSERT(utf8_bytes_filled > 0);
|
||||
JSON_ASSERT(utf8_bytes_index == 0);
|
||||
}
|
||||
|
||||
// use buffer
|
||||
JSON_ASSERT(utf8_bytes_filled > 0);
|
||||
JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
|
||||
return utf8_bytes[utf8_bytes_index++];
|
||||
}
|
||||
|
||||
private:
|
||||
BaseInputAdapter base_adapter;
|
||||
|
||||
template<size_t T>
|
||||
void fill_buffer()
|
||||
{
|
||||
wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
|
||||
}
|
||||
|
||||
/// a buffer for UTF-8 bytes
|
||||
std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
|
||||
|
||||
/// index to the utf8_codes array for the next valid byte
|
||||
std::size_t utf8_bytes_index = 0;
|
||||
/// number of valid bytes in the utf8_codes array
|
||||
std::size_t utf8_bytes_filled = 0;
|
||||
};
|
||||
|
||||
template<typename IteratorType, typename Enable = void>
|
||||
struct iterator_input_adapter_factory
|
||||
{
|
||||
using iterator_type = IteratorType;
|
||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||
using adapter_type = iterator_input_adapter<iterator_type>;
|
||||
|
||||
static adapter_type create(IteratorType first, IteratorType last)
|
||||
{
|
||||
return adapter_type(std::move(first), std::move(last));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_iterator_of_multibyte
|
||||
{
|
||||
using value_type = typename std::iterator_traits<T>::value_type;
|
||||
enum
|
||||
{
|
||||
value = sizeof(value_type) > 1
|
||||
};
|
||||
};
|
||||
|
||||
template<typename IteratorType>
|
||||
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
||||
{
|
||||
using iterator_type = IteratorType;
|
||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||
using base_adapter_type = iterator_input_adapter<iterator_type>;
|
||||
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
|
||||
|
||||
static adapter_type create(IteratorType first, IteratorType last)
|
||||
{
|
||||
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
|
||||
}
|
||||
};
|
||||
|
||||
// General purpose iterator-based input
|
||||
template<typename IteratorType>
|
||||
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
|
||||
{
|
||||
using factory_type = iterator_input_adapter_factory<IteratorType>;
|
||||
return factory_type::create(first, last);
|
||||
}
|
||||
|
||||
// Convenience shorthand from container to iterator
|
||||
// Enables ADL on begin(container) and end(container)
|
||||
// Encloses the using declarations in namespace for not to leak them to outside scope
|
||||
|
||||
namespace container_input_adapter_factory_impl
|
||||
{
|
||||
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
template<typename ContainerType, typename Enable = void>
|
||||
struct container_input_adapter_factory {};
|
||||
|
||||
template<typename ContainerType>
|
||||
struct container_input_adapter_factory< ContainerType,
|
||||
void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>
|
||||
{
|
||||
using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
|
||||
|
||||
static adapter_type create(const ContainerType& container)
|
||||
{
|
||||
return input_adapter(begin(container), end(container));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace container_input_adapter_factory_impl
|
||||
|
||||
template<typename ContainerType>
|
||||
typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
|
||||
{
|
||||
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
|
||||
}
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
// Special cases with fast paths
|
||||
inline file_input_adapter input_adapter(std::FILE* file)
|
||||
{
|
||||
return file_input_adapter(file);
|
||||
}
|
||||
|
||||
inline input_stream_adapter input_adapter(std::istream& stream)
|
||||
{
|
||||
return input_stream_adapter(stream);
|
||||
}
|
||||
|
||||
inline input_stream_adapter input_adapter(std::istream&& stream)
|
||||
{
|
||||
return input_stream_adapter(stream);
|
||||
}
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
|
||||
|
||||
// Null-delimited strings, and the like.
|
||||
template < typename CharT,
|
||||
typename std::enable_if <
|
||||
std::is_pointer<CharT>::value&&
|
||||
!std::is_array<CharT>::value&&
|
||||
std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
|
||||
sizeof(typename std::remove_pointer<CharT>::type) == 1,
|
||||
int >::type = 0 >
|
||||
contiguous_bytes_input_adapter input_adapter(CharT b)
|
||||
{
|
||||
auto length = std::strlen(reinterpret_cast<const char*>(b));
|
||||
const auto* ptr = reinterpret_cast<const char*>(b);
|
||||
return input_adapter(ptr, ptr + length);
|
||||
}
|
||||
|
||||
template<typename T, std::size_t N>
|
||||
auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
{
|
||||
return input_adapter(array, array + N);
|
||||
}
|
||||
|
||||
// This class only handles inputs of input_buffer_adapter type.
|
||||
// It's required so that expressions like {ptr, len} can be implicitly cast
|
||||
// to the correct adapter.
|
||||
class span_input_adapter
|
||||
{
|
||||
public:
|
||||
template < typename CharT,
|
||||
typename std::enable_if <
|
||||
std::is_pointer<CharT>::value&&
|
||||
std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
|
||||
sizeof(typename std::remove_pointer<CharT>::type) == 1,
|
||||
int >::type = 0 >
|
||||
span_input_adapter(CharT b, std::size_t l)
|
||||
: ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
|
||||
|
||||
template<class IteratorType,
|
||||
typename std::enable_if<
|
||||
std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
|
||||
int>::type = 0>
|
||||
span_input_adapter(IteratorType first, IteratorType last)
|
||||
: ia(input_adapter(first, last)) {}
|
||||
|
||||
contiguous_bytes_input_adapter&& get()
|
||||
{
|
||||
return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
|
||||
}
|
||||
|
||||
private:
|
||||
contiguous_bytes_input_adapter ia;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,727 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string> // string
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/*!
|
||||
@brief SAX interface
|
||||
|
||||
This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
|
||||
Each function is called in different situations while the input is parsed. The
|
||||
boolean return value informs the parser whether to continue processing the
|
||||
input.
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
struct json_sax
|
||||
{
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
|
||||
/*!
|
||||
@brief a null value was read
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool null() = 0;
|
||||
|
||||
/*!
|
||||
@brief a boolean value was read
|
||||
@param[in] val boolean value
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool boolean(bool val) = 0;
|
||||
|
||||
/*!
|
||||
@brief an integer number was read
|
||||
@param[in] val integer value
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool number_integer(number_integer_t val) = 0;
|
||||
|
||||
/*!
|
||||
@brief an unsigned integer number was read
|
||||
@param[in] val unsigned integer value
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool number_unsigned(number_unsigned_t val) = 0;
|
||||
|
||||
/*!
|
||||
@brief a floating-point number was read
|
||||
@param[in] val floating-point value
|
||||
@param[in] s raw token value
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool number_float(number_float_t val, const string_t& s) = 0;
|
||||
|
||||
/*!
|
||||
@brief a string value was read
|
||||
@param[in] val string value
|
||||
@return whether parsing should proceed
|
||||
@note It is safe to move the passed string value.
|
||||
*/
|
||||
virtual bool string(string_t& val) = 0;
|
||||
|
||||
/*!
|
||||
@brief a binary value was read
|
||||
@param[in] val binary value
|
||||
@return whether parsing should proceed
|
||||
@note It is safe to move the passed binary value.
|
||||
*/
|
||||
virtual bool binary(binary_t& val) = 0;
|
||||
|
||||
/*!
|
||||
@brief the beginning of an object was read
|
||||
@param[in] elements number of object elements or -1 if unknown
|
||||
@return whether parsing should proceed
|
||||
@note binary formats may report the number of elements
|
||||
*/
|
||||
virtual bool start_object(std::size_t elements) = 0;
|
||||
|
||||
/*!
|
||||
@brief an object key was read
|
||||
@param[in] val object key
|
||||
@return whether parsing should proceed
|
||||
@note It is safe to move the passed string.
|
||||
*/
|
||||
virtual bool key(string_t& val) = 0;
|
||||
|
||||
/*!
|
||||
@brief the end of an object was read
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool end_object() = 0;
|
||||
|
||||
/*!
|
||||
@brief the beginning of an array was read
|
||||
@param[in] elements number of array elements or -1 if unknown
|
||||
@return whether parsing should proceed
|
||||
@note binary formats may report the number of elements
|
||||
*/
|
||||
virtual bool start_array(std::size_t elements) = 0;
|
||||
|
||||
/*!
|
||||
@brief the end of an array was read
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool end_array() = 0;
|
||||
|
||||
/*!
|
||||
@brief a parse error occurred
|
||||
@param[in] position the position in the input where the error occurs
|
||||
@param[in] last_token the last read token
|
||||
@param[in] ex an exception object describing the error
|
||||
@return whether parsing should proceed (must return false)
|
||||
*/
|
||||
virtual bool parse_error(std::size_t position,
|
||||
const std::string& last_token,
|
||||
const detail::exception& ex) = 0;
|
||||
|
||||
json_sax() = default;
|
||||
json_sax(const json_sax&) = default;
|
||||
json_sax(json_sax&&) noexcept = default;
|
||||
json_sax& operator=(const json_sax&) = default;
|
||||
json_sax& operator=(json_sax&&) noexcept = default;
|
||||
virtual ~json_sax() = default;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
/*!
|
||||
@brief SAX implementation to create a JSON value from SAX events
|
||||
|
||||
This class implements the @ref json_sax interface and processes the SAX events
|
||||
to create a JSON value which makes it basically a DOM parser. The structure or
|
||||
hierarchy of the JSON value is managed by the stack `ref_stack` which contains
|
||||
a pointer to the respective array or object for each recursion depth.
|
||||
|
||||
After successful parsing, the value that is passed by reference to the
|
||||
constructor contains the parsed value.
|
||||
|
||||
@tparam BasicJsonType the JSON type
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
class json_sax_dom_parser
|
||||
{
|
||||
public:
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
|
||||
/*!
|
||||
@param[in,out] r reference to a JSON value that is manipulated while
|
||||
parsing
|
||||
@param[in] allow_exceptions_ whether parse errors yield exceptions
|
||||
*/
|
||||
explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
|
||||
: root(r), allow_exceptions(allow_exceptions_)
|
||||
{}
|
||||
|
||||
// make class move-only
|
||||
json_sax_dom_parser(const json_sax_dom_parser&) = delete;
|
||||
json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
|
||||
json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;
|
||||
json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
|
||||
~json_sax_dom_parser() = default;
|
||||
|
||||
bool null()
|
||||
{
|
||||
handle_value(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool val)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(number_integer_t val)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_unsigned(number_unsigned_t val)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(number_float_t val, const string_t& /*unused*/)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(string_t& val)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool binary(binary_t& val)
|
||||
{
|
||||
handle_value(std::move(val));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t len)
|
||||
{
|
||||
ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool key(string_t& val)
|
||||
{
|
||||
JSON_ASSERT(!ref_stack.empty());
|
||||
JSON_ASSERT(ref_stack.back()->is_object());
|
||||
|
||||
// add null at given key and store the reference for later
|
||||
object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object()
|
||||
{
|
||||
JSON_ASSERT(!ref_stack.empty());
|
||||
JSON_ASSERT(ref_stack.back()->is_object());
|
||||
|
||||
ref_stack.back()->set_parents();
|
||||
ref_stack.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t len)
|
||||
{
|
||||
ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array()
|
||||
{
|
||||
JSON_ASSERT(!ref_stack.empty());
|
||||
JSON_ASSERT(ref_stack.back()->is_array());
|
||||
|
||||
ref_stack.back()->set_parents();
|
||||
ref_stack.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class Exception>
|
||||
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
|
||||
const Exception& ex)
|
||||
{
|
||||
errored = true;
|
||||
static_cast<void>(ex);
|
||||
if (allow_exceptions)
|
||||
{
|
||||
JSON_THROW(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is_errored() const
|
||||
{
|
||||
return errored;
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@invariant If the ref stack is empty, then the passed value will be the new
|
||||
root.
|
||||
@invariant If the ref stack contains a value, then it is an array or an
|
||||
object to which we can add elements
|
||||
*/
|
||||
template<typename Value>
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
BasicJsonType* handle_value(Value&& v)
|
||||
{
|
||||
if (ref_stack.empty())
|
||||
{
|
||||
root = BasicJsonType(std::forward<Value>(v));
|
||||
return &root;
|
||||
}
|
||||
|
||||
JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
|
||||
|
||||
if (ref_stack.back()->is_array())
|
||||
{
|
||||
ref_stack.back()->m_data.m_value.array->emplace_back(std::forward<Value>(v));
|
||||
return &(ref_stack.back()->m_data.m_value.array->back());
|
||||
}
|
||||
|
||||
JSON_ASSERT(ref_stack.back()->is_object());
|
||||
JSON_ASSERT(object_element);
|
||||
*object_element = BasicJsonType(std::forward<Value>(v));
|
||||
return object_element;
|
||||
}
|
||||
|
||||
/// the parsed JSON value
|
||||
BasicJsonType& root;
|
||||
/// stack to model hierarchy of values
|
||||
std::vector<BasicJsonType*> ref_stack {};
|
||||
/// helper to hold the reference for the next object element
|
||||
BasicJsonType* object_element = nullptr;
|
||||
/// whether a syntax error occurred
|
||||
bool errored = false;
|
||||
/// whether to throw exceptions in case of errors
|
||||
const bool allow_exceptions = true;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class json_sax_dom_callback_parser
|
||||
{
|
||||
public:
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
using parser_callback_t = typename BasicJsonType::parser_callback_t;
|
||||
using parse_event_t = typename BasicJsonType::parse_event_t;
|
||||
|
||||
json_sax_dom_callback_parser(BasicJsonType& r,
|
||||
const parser_callback_t cb,
|
||||
const bool allow_exceptions_ = true)
|
||||
: root(r), callback(cb), allow_exceptions(allow_exceptions_)
|
||||
{
|
||||
keep_stack.push_back(true);
|
||||
}
|
||||
|
||||
// make class move-only
|
||||
json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
|
||||
json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
|
||||
json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;
|
||||
json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
|
||||
~json_sax_dom_callback_parser() = default;
|
||||
|
||||
bool null()
|
||||
{
|
||||
handle_value(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool val)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(number_integer_t val)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_unsigned(number_unsigned_t val)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(number_float_t val, const string_t& /*unused*/)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(string_t& val)
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool binary(binary_t& val)
|
||||
{
|
||||
handle_value(std::move(val));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t len)
|
||||
{
|
||||
// check callback for object start
|
||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
|
||||
keep_stack.push_back(keep);
|
||||
|
||||
auto val = handle_value(BasicJsonType::value_t::object, true);
|
||||
ref_stack.push_back(val.second);
|
||||
|
||||
// check object limit
|
||||
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool key(string_t& val)
|
||||
{
|
||||
BasicJsonType k = BasicJsonType(val);
|
||||
|
||||
// check callback for key
|
||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
|
||||
key_keep_stack.push_back(keep);
|
||||
|
||||
// add discarded value at given key and store the reference for later
|
||||
if (keep && ref_stack.back())
|
||||
{
|
||||
object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val) = discarded);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object()
|
||||
{
|
||||
if (ref_stack.back())
|
||||
{
|
||||
if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
|
||||
{
|
||||
// discard object
|
||||
*ref_stack.back() = discarded;
|
||||
}
|
||||
else
|
||||
{
|
||||
ref_stack.back()->set_parents();
|
||||
}
|
||||
}
|
||||
|
||||
JSON_ASSERT(!ref_stack.empty());
|
||||
JSON_ASSERT(!keep_stack.empty());
|
||||
ref_stack.pop_back();
|
||||
keep_stack.pop_back();
|
||||
|
||||
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
|
||||
{
|
||||
// remove discarded value
|
||||
for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
|
||||
{
|
||||
if (it->is_discarded())
|
||||
{
|
||||
ref_stack.back()->erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t len)
|
||||
{
|
||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
|
||||
keep_stack.push_back(keep);
|
||||
|
||||
auto val = handle_value(BasicJsonType::value_t::array, true);
|
||||
ref_stack.push_back(val.second);
|
||||
|
||||
// check array limit
|
||||
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array()
|
||||
{
|
||||
bool keep = true;
|
||||
|
||||
if (ref_stack.back())
|
||||
{
|
||||
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
|
||||
if (keep)
|
||||
{
|
||||
ref_stack.back()->set_parents();
|
||||
}
|
||||
else
|
||||
{
|
||||
// discard array
|
||||
*ref_stack.back() = discarded;
|
||||
}
|
||||
}
|
||||
|
||||
JSON_ASSERT(!ref_stack.empty());
|
||||
JSON_ASSERT(!keep_stack.empty());
|
||||
ref_stack.pop_back();
|
||||
keep_stack.pop_back();
|
||||
|
||||
// remove discarded value
|
||||
if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
|
||||
{
|
||||
ref_stack.back()->m_data.m_value.array->pop_back();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class Exception>
|
||||
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
|
||||
const Exception& ex)
|
||||
{
|
||||
errored = true;
|
||||
static_cast<void>(ex);
|
||||
if (allow_exceptions)
|
||||
{
|
||||
JSON_THROW(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is_errored() const
|
||||
{
|
||||
return errored;
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@param[in] v value to add to the JSON value we build during parsing
|
||||
@param[in] skip_callback whether we should skip calling the callback
|
||||
function; this is required after start_array() and
|
||||
start_object() SAX events, because otherwise we would call the
|
||||
callback function with an empty array or object, respectively.
|
||||
|
||||
@invariant If the ref stack is empty, then the passed value will be the new
|
||||
root.
|
||||
@invariant If the ref stack contains a value, then it is an array or an
|
||||
object to which we can add elements
|
||||
|
||||
@return pair of boolean (whether value should be kept) and pointer (to the
|
||||
passed value in the ref_stack hierarchy; nullptr if not kept)
|
||||
*/
|
||||
template<typename Value>
|
||||
std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
|
||||
{
|
||||
JSON_ASSERT(!keep_stack.empty());
|
||||
|
||||
// do not handle this value if we know it would be added to a discarded
|
||||
// container
|
||||
if (!keep_stack.back())
|
||||
{
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
// create value
|
||||
auto value = BasicJsonType(std::forward<Value>(v));
|
||||
|
||||
// check callback
|
||||
const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
|
||||
|
||||
// do not handle this value if we just learnt it shall be discarded
|
||||
if (!keep)
|
||||
{
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
if (ref_stack.empty())
|
||||
{
|
||||
root = std::move(value);
|
||||
return {true, & root};
|
||||
}
|
||||
|
||||
// skip this value if we already decided to skip the parent
|
||||
// (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
|
||||
if (!ref_stack.back())
|
||||
{
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
// we now only expect arrays and objects
|
||||
JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
|
||||
|
||||
// array
|
||||
if (ref_stack.back()->is_array())
|
||||
{
|
||||
ref_stack.back()->m_data.m_value.array->emplace_back(std::move(value));
|
||||
return {true, & (ref_stack.back()->m_data.m_value.array->back())};
|
||||
}
|
||||
|
||||
// object
|
||||
JSON_ASSERT(ref_stack.back()->is_object());
|
||||
// check if we should store an element for the current key
|
||||
JSON_ASSERT(!key_keep_stack.empty());
|
||||
const bool store_element = key_keep_stack.back();
|
||||
key_keep_stack.pop_back();
|
||||
|
||||
if (!store_element)
|
||||
{
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
JSON_ASSERT(object_element);
|
||||
*object_element = std::move(value);
|
||||
return {true, object_element};
|
||||
}
|
||||
|
||||
/// the parsed JSON value
|
||||
BasicJsonType& root;
|
||||
/// stack to model hierarchy of values
|
||||
std::vector<BasicJsonType*> ref_stack {};
|
||||
/// stack to manage which values to keep
|
||||
std::vector<bool> keep_stack {}; // NOLINT(readability-redundant-member-init)
|
||||
/// stack to manage which object keys to keep
|
||||
std::vector<bool> key_keep_stack {}; // NOLINT(readability-redundant-member-init)
|
||||
/// helper to hold the reference for the next object element
|
||||
BasicJsonType* object_element = nullptr;
|
||||
/// whether a syntax error occurred
|
||||
bool errored = false;
|
||||
/// callback function
|
||||
const parser_callback_t callback = nullptr;
|
||||
/// whether to throw exceptions in case of errors
|
||||
const bool allow_exceptions = true;
|
||||
/// a discarded value for the callback
|
||||
BasicJsonType discarded = BasicJsonType::value_t::discarded;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class json_sax_acceptor
|
||||
{
|
||||
public:
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
|
||||
bool null()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool /*unused*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(number_integer_t /*unused*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_unsigned(number_unsigned_t /*unused*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(string_t& /*unused*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool binary(binary_t& /*unused*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool key(string_t& /*unused*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,519 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath> // isfinite
|
||||
#include <cstdint> // uint8_t
|
||||
#include <functional> // function
|
||||
#include <string> // string
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||
#include <nlohmann/detail/input/json_sax.hpp>
|
||||
#include <nlohmann/detail/input/lexer.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/is_sax.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
////////////
|
||||
// parser //
|
||||
////////////
|
||||
|
||||
enum class parse_event_t : std::uint8_t
|
||||
{
|
||||
/// the parser read `{` and started to process a JSON object
|
||||
object_start,
|
||||
/// the parser read `}` and finished processing a JSON object
|
||||
object_end,
|
||||
/// the parser read `[` and started to process a JSON array
|
||||
array_start,
|
||||
/// the parser read `]` and finished processing a JSON array
|
||||
array_end,
|
||||
/// the parser read a key of a value in an object
|
||||
key,
|
||||
/// the parser finished reading a JSON value
|
||||
value
|
||||
};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
using parser_callback_t =
|
||||
std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
|
||||
|
||||
/*!
|
||||
@brief syntax analysis
|
||||
|
||||
This class implements a recursive descent parser.
|
||||
*/
|
||||
template<typename BasicJsonType, typename InputAdapterType>
|
||||
class parser
|
||||
{
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using lexer_t = lexer<BasicJsonType, InputAdapterType>;
|
||||
using token_type = typename lexer_t::token_type;
|
||||
|
||||
public:
|
||||
/// a parser reading from an input adapter
|
||||
explicit parser(InputAdapterType&& adapter,
|
||||
const parser_callback_t<BasicJsonType> cb = nullptr,
|
||||
const bool allow_exceptions_ = true,
|
||||
const bool skip_comments = false)
|
||||
: callback(cb)
|
||||
, m_lexer(std::move(adapter), skip_comments)
|
||||
, allow_exceptions(allow_exceptions_)
|
||||
{
|
||||
// read first token
|
||||
get_token();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief public parser interface
|
||||
|
||||
@param[in] strict whether to expect the last token to be EOF
|
||||
@param[in,out] result parsed JSON value
|
||||
|
||||
@throw parse_error.101 in case of an unexpected token
|
||||
@throw parse_error.102 if to_unicode fails or surrogate error
|
||||
@throw parse_error.103 if to_unicode fails
|
||||
*/
|
||||
void parse(const bool strict, BasicJsonType& result)
|
||||
{
|
||||
if (callback)
|
||||
{
|
||||
json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
|
||||
sax_parse_internal(&sdp);
|
||||
|
||||
// in strict mode, input must be completely read
|
||||
if (strict && (get_token() != token_type::end_of_input))
|
||||
{
|
||||
sdp.parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(),
|
||||
exception_message(token_type::end_of_input, "value"), nullptr));
|
||||
}
|
||||
|
||||
// in case of an error, return discarded value
|
||||
if (sdp.is_errored())
|
||||
{
|
||||
result = value_t::discarded;
|
||||
return;
|
||||
}
|
||||
|
||||
// set top-level value to null if it was discarded by the callback
|
||||
// function
|
||||
if (result.is_discarded())
|
||||
{
|
||||
result = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
|
||||
sax_parse_internal(&sdp);
|
||||
|
||||
// in strict mode, input must be completely read
|
||||
if (strict && (get_token() != token_type::end_of_input))
|
||||
{
|
||||
sdp.parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
|
||||
}
|
||||
|
||||
// in case of an error, return discarded value
|
||||
if (sdp.is_errored())
|
||||
{
|
||||
result = value_t::discarded;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
result.assert_invariant();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief public accept interface
|
||||
|
||||
@param[in] strict whether to expect the last token to be EOF
|
||||
@return whether the input is a proper JSON text
|
||||
*/
|
||||
bool accept(const bool strict = true)
|
||||
{
|
||||
json_sax_acceptor<BasicJsonType> sax_acceptor;
|
||||
return sax_parse(&sax_acceptor, strict);
|
||||
}
|
||||
|
||||
template<typename SAX>
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
bool sax_parse(SAX* sax, const bool strict = true)
|
||||
{
|
||||
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
||||
const bool result = sax_parse_internal(sax);
|
||||
|
||||
// strict mode: next byte must be EOF
|
||||
if (result && strict && (get_token() != token_type::end_of_input))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename SAX>
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
bool sax_parse_internal(SAX* sax)
|
||||
{
|
||||
// stack to remember the hierarchy of structured values we are parsing
|
||||
// true = array; false = object
|
||||
std::vector<bool> states;
|
||||
// value to avoid a goto (see comment where set to true)
|
||||
bool skip_to_state_evaluation = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!skip_to_state_evaluation)
|
||||
{
|
||||
// invariant: get_token() was called before each iteration
|
||||
switch (last_token)
|
||||
{
|
||||
case token_type::begin_object:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// closing } -> we are done
|
||||
if (get_token() == token_type::end_object)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// parse key
|
||||
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse separator (:)
|
||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
|
||||
}
|
||||
|
||||
// remember we are now inside an object
|
||||
states.push_back(false);
|
||||
|
||||
// parse values
|
||||
get_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
case token_type::begin_array:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// closing ] -> we are done
|
||||
if (get_token() == token_type::end_array)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// remember we are now inside an array
|
||||
states.push_back(true);
|
||||
|
||||
// parse values (no need to call get_token)
|
||||
continue;
|
||||
}
|
||||
|
||||
case token_type::value_float:
|
||||
{
|
||||
const auto res = m_lexer.get_number_float();
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::literal_false:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::literal_null:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->null()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::literal_true:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::value_integer:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::value_string:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::value_unsigned:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::parse_error:
|
||||
{
|
||||
// using "uninitialized" to avoid "expected" message
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
|
||||
}
|
||||
case token_type::end_of_input:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(),
|
||||
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
|
||||
}
|
||||
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
|
||||
}
|
||||
case token_type::uninitialized:
|
||||
case token_type::end_array:
|
||||
case token_type::end_object:
|
||||
case token_type::name_separator:
|
||||
case token_type::value_separator:
|
||||
case token_type::literal_or_value:
|
||||
default: // the last token was unexpected
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
skip_to_state_evaluation = false;
|
||||
}
|
||||
|
||||
// we reached this line after we successfully parsed a value
|
||||
if (states.empty())
|
||||
{
|
||||
// empty stack: we reached the end of the hierarchy: done
|
||||
return true;
|
||||
}
|
||||
|
||||
if (states.back()) // array
|
||||
{
|
||||
// comma -> next value
|
||||
if (get_token() == token_type::value_separator)
|
||||
{
|
||||
// parse a new value
|
||||
get_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
// closing ]
|
||||
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are done with this array. Before we can parse a
|
||||
// new value, we need to evaluate the new state first.
|
||||
// By setting skip_to_state_evaluation to false, we
|
||||
// are effectively jumping to the beginning of this if.
|
||||
JSON_ASSERT(!states.empty());
|
||||
states.pop_back();
|
||||
skip_to_state_evaluation = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
|
||||
}
|
||||
|
||||
// states.back() is false -> object
|
||||
|
||||
// comma -> next value
|
||||
if (get_token() == token_type::value_separator)
|
||||
{
|
||||
// parse key
|
||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse separator (:)
|
||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
|
||||
}
|
||||
|
||||
// parse values
|
||||
get_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
// closing }
|
||||
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are done with this object. Before we can parse a
|
||||
// new value, we need to evaluate the new state first.
|
||||
// By setting skip_to_state_evaluation to false, we
|
||||
// are effectively jumping to the beginning of this if.
|
||||
JSON_ASSERT(!states.empty());
|
||||
states.pop_back();
|
||||
skip_to_state_evaluation = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
/// get next token from lexer
|
||||
token_type get_token()
|
||||
{
|
||||
return last_token = m_lexer.scan();
|
||||
}
|
||||
|
||||
std::string exception_message(const token_type expected, const std::string& context)
|
||||
{
|
||||
std::string error_msg = "syntax error ";
|
||||
|
||||
if (!context.empty())
|
||||
{
|
||||
error_msg += concat("while parsing ", context, ' ');
|
||||
}
|
||||
|
||||
error_msg += "- ";
|
||||
|
||||
if (last_token == token_type::parse_error)
|
||||
{
|
||||
error_msg += concat(m_lexer.get_error_message(), "; last read: '",
|
||||
m_lexer.get_token_string(), '\'');
|
||||
}
|
||||
else
|
||||
{
|
||||
error_msg += concat("unexpected ", lexer_t::token_type_name(last_token));
|
||||
}
|
||||
|
||||
if (expected != token_type::uninitialized)
|
||||
{
|
||||
error_msg += concat("; expected ", lexer_t::token_type_name(expected));
|
||||
}
|
||||
|
||||
return error_msg;
|
||||
}
|
||||
|
||||
private:
|
||||
/// callback function
|
||||
const parser_callback_t<BasicJsonType> callback = nullptr;
|
||||
/// the type of the last read token
|
||||
token_type last_token = token_type::uninitialized;
|
||||
/// the lexer
|
||||
lexer_t m_lexer;
|
||||
/// whether to throw exceptions in case of errors
|
||||
const bool allow_exceptions = true;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,37 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // size_t
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// struct to capture the start position of the current token
|
||||
struct position_t
|
||||
{
|
||||
/// the total number of characters read
|
||||
std::size_t chars_read_total = 0;
|
||||
/// the number of characters read in the current line
|
||||
std::size_t chars_read_current_line = 0;
|
||||
/// the number of lines read
|
||||
std::size_t lines_read = 0;
|
||||
|
||||
/// conversion to size_t to preserve SAX interface
|
||||
constexpr operator size_t() const
|
||||
{
|
||||
return chars_read_total;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,35 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/*!
|
||||
@brief an iterator value
|
||||
|
||||
@note This structure could easily be a union, but MSVC currently does not allow
|
||||
unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
|
||||
*/
|
||||
template<typename BasicJsonType> struct internal_iterator
|
||||
{
|
||||
/// iterator for JSON objects
|
||||
typename BasicJsonType::object_t::iterator object_iterator {};
|
||||
/// iterator for JSON arrays
|
||||
typename BasicJsonType::array_t::iterator array_iterator {};
|
||||
/// generic iterator for all other types
|
||||
primitive_iterator_t primitive_iterator {};
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,751 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
|
||||
#include <type_traits> // conditional, is_const, remove_const
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/iterators/internal_iterator.hpp>
|
||||
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// forward declare, to be able to friend it later on
|
||||
template<typename IteratorType> class iteration_proxy;
|
||||
template<typename IteratorType> class iteration_proxy_value;
|
||||
|
||||
/*!
|
||||
@brief a template for a bidirectional iterator for the @ref basic_json class
|
||||
This class implements a both iterators (iterator and const_iterator) for the
|
||||
@ref basic_json class.
|
||||
@note An iterator is called *initialized* when a pointer to a JSON value has
|
||||
been set (e.g., by a constructor or a copy assignment). If the iterator is
|
||||
default-constructed, it is *uninitialized* and most methods are undefined.
|
||||
**The library uses assertions to detect calls on uninitialized iterators.**
|
||||
@requirement The class satisfies the following concept requirements:
|
||||
-
|
||||
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
|
||||
The iterator that can be moved can be moved in both directions (i.e.
|
||||
incremented and decremented).
|
||||
@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
|
||||
iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
||||
{
|
||||
/// the iterator with BasicJsonType of different const-ness
|
||||
using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
|
||||
/// allow basic_json to access private members
|
||||
friend other_iter_impl;
|
||||
friend BasicJsonType;
|
||||
friend iteration_proxy<iter_impl>;
|
||||
friend iteration_proxy_value<iter_impl>;
|
||||
|
||||
using object_t = typename BasicJsonType::object_t;
|
||||
using array_t = typename BasicJsonType::array_t;
|
||||
// make sure BasicJsonType is basic_json or const basic_json
|
||||
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
|
||||
"iter_impl only accepts (const) basic_json");
|
||||
// superficial check for the LegacyBidirectionalIterator named requirement
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
|
||||
&& std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,
|
||||
"basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
|
||||
|
||||
public:
|
||||
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
|
||||
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
|
||||
/// A user-defined iterator should provide publicly accessible typedefs named
|
||||
/// iterator_category, value_type, difference_type, pointer, and reference.
|
||||
/// Note that value_type is required to be non-const, even for constant iterators.
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
/// the type of the values when the iterator is dereferenced
|
||||
using value_type = typename BasicJsonType::value_type;
|
||||
/// a type to represent differences between iterators
|
||||
using difference_type = typename BasicJsonType::difference_type;
|
||||
/// defines a pointer to the type iterated over (value_type)
|
||||
using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
|
||||
typename BasicJsonType::const_pointer,
|
||||
typename BasicJsonType::pointer>::type;
|
||||
/// defines a reference to the type iterated over (value_type)
|
||||
using reference =
|
||||
typename std::conditional<std::is_const<BasicJsonType>::value,
|
||||
typename BasicJsonType::const_reference,
|
||||
typename BasicJsonType::reference>::type;
|
||||
|
||||
iter_impl() = default;
|
||||
~iter_impl() = default;
|
||||
iter_impl(iter_impl&&) noexcept = default;
|
||||
iter_impl& operator=(iter_impl&&) noexcept = default;
|
||||
|
||||
/*!
|
||||
@brief constructor for a given JSON instance
|
||||
@param[in] object pointer to a JSON object for this iterator
|
||||
@pre object != nullptr
|
||||
@post The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
explicit iter_impl(pointer object) noexcept : m_object(object)
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
m_it.object_iterator = typename object_t::iterator();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
m_it.array_iterator = typename array_t::iterator();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
m_it.primitive_iterator = primitive_iterator_t();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@note The conventional copy constructor and copy assignment are implicitly
|
||||
defined. Combined with the following converting constructor and
|
||||
assignment, they support: (1) copy from iterator to iterator, (2)
|
||||
copy from const iterator to const iterator, and (3) conversion from
|
||||
iterator to const iterator. However conversion from const iterator
|
||||
to iterator is not defined.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@brief const copy constructor
|
||||
@param[in] other const iterator to copy from
|
||||
@note This copy constructor had to be defined explicitly to circumvent a bug
|
||||
occurring on msvc v19.0 compiler (VS 2015) debug build. For more
|
||||
information refer to: https://github.com/nlohmann/json/issues/1608
|
||||
*/
|
||||
iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
|
||||
: m_object(other.m_object), m_it(other.m_it)
|
||||
{}
|
||||
|
||||
/*!
|
||||
@brief converting assignment
|
||||
@param[in] other const iterator to copy from
|
||||
@return const/non-const iterator
|
||||
@note It is not checked whether @a other is initialized.
|
||||
*/
|
||||
iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
m_object = other.m_object;
|
||||
m_it = other.m_it;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief converting constructor
|
||||
@param[in] other non-const iterator to copy from
|
||||
@note It is not checked whether @a other is initialized.
|
||||
*/
|
||||
iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
|
||||
: m_object(other.m_object), m_it(other.m_it)
|
||||
{}
|
||||
|
||||
/*!
|
||||
@brief converting assignment
|
||||
@param[in] other non-const iterator to copy from
|
||||
@return const/non-const iterator
|
||||
@note It is not checked whether @a other is initialized.
|
||||
*/
|
||||
iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)
|
||||
{
|
||||
m_object = other.m_object;
|
||||
m_it = other.m_it;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
/*!
|
||||
@brief set the iterator to the first value
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
void set_begin() noexcept
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
m_it.object_iterator = m_object->m_data.m_value.object->begin();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
m_it.array_iterator = m_object->m_data.m_value.array->begin();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
{
|
||||
// set to end so begin()==end() is true: null is empty
|
||||
m_it.primitive_iterator.set_end();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
m_it.primitive_iterator.set_begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief set the iterator past the last value
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
void set_end() noexcept
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
m_it.object_iterator = m_object->m_data.m_value.object->end();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
m_it.array_iterator = m_object->m_data.m_value.array->end();
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
m_it.primitive_iterator.set_end();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*!
|
||||
@brief return a reference to the value pointed to by the iterator
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
reference operator*() const
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
|
||||
return m_it.object_iterator->second;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
|
||||
return *m_it.array_iterator;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
|
||||
{
|
||||
return *m_object;
|
||||
}
|
||||
|
||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief dereference the iterator
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
pointer operator->() const
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
|
||||
return &(m_it.object_iterator->second);
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
|
||||
return &*m_it.array_iterator;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
|
||||
{
|
||||
return m_object;
|
||||
}
|
||||
|
||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief post-increment (it++)
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp)
|
||||
{
|
||||
auto result = *this;
|
||||
++(*this);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief pre-increment (++it)
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
iter_impl& operator++()
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
std::advance(m_it.object_iterator, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
std::advance(m_it.array_iterator, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
++m_it.primitive_iterator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief post-decrement (it--)
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp)
|
||||
{
|
||||
auto result = *this;
|
||||
--(*this);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief pre-decrement (--it)
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
iter_impl& operator--()
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
std::advance(m_it.object_iterator, -1);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
std::advance(m_it.array_iterator, -1);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
--m_it.primitive_iterator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief comparison: equal
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
|
||||
bool operator==(const IterImpl& other) const
|
||||
{
|
||||
// if objects are not the same, the comparison is undefined
|
||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
|
||||
}
|
||||
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
return (m_it.object_iterator == other.m_it.object_iterator);
|
||||
|
||||
case value_t::array:
|
||||
return (m_it.array_iterator == other.m_it.array_iterator);
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return (m_it.primitive_iterator == other.m_it.primitive_iterator);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief comparison: not equal
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
|
||||
bool operator!=(const IterImpl& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief comparison: smaller
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
bool operator<(const iter_impl& other) const
|
||||
{
|
||||
// if objects are not the same, the comparison is undefined
|
||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
||||
{
|
||||
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
|
||||
}
|
||||
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
|
||||
|
||||
case value_t::array:
|
||||
return (m_it.array_iterator < other.m_it.array_iterator);
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return (m_it.primitive_iterator < other.m_it.primitive_iterator);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief comparison: less than or equal
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
bool operator<=(const iter_impl& other) const
|
||||
{
|
||||
return !other.operator < (*this);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief comparison: greater than
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
bool operator>(const iter_impl& other) const
|
||||
{
|
||||
return !operator<=(other);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief comparison: greater than or equal
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
bool operator>=(const iter_impl& other) const
|
||||
{
|
||||
return !operator<(other);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief add to iterator
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
iter_impl& operator+=(difference_type i)
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
std::advance(m_it.array_iterator, i);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
m_it.primitive_iterator += i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief subtract from iterator
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
iter_impl& operator-=(difference_type i)
|
||||
{
|
||||
return operator+=(-i);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief add to iterator
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
iter_impl operator+(difference_type i) const
|
||||
{
|
||||
auto result = *this;
|
||||
result += i;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief addition of distance and iterator
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
friend iter_impl operator+(difference_type i, const iter_impl& it)
|
||||
{
|
||||
auto result = it;
|
||||
result += i;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief subtract from iterator
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
iter_impl operator-(difference_type i) const
|
||||
{
|
||||
auto result = *this;
|
||||
result -= i;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief return difference
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
difference_type operator-(const iter_impl& other) const
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
|
||||
|
||||
case value_t::array:
|
||||
return m_it.array_iterator - other.m_it.array_iterator;
|
||||
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return m_it.primitive_iterator - other.m_it.primitive_iterator;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief access to successor
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
reference operator[](difference_type n) const
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
switch (m_object->m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
|
||||
|
||||
case value_t::array:
|
||||
return *std::next(m_it.array_iterator, n);
|
||||
|
||||
case value_t::null:
|
||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
{
|
||||
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))
|
||||
{
|
||||
return *m_object;
|
||||
}
|
||||
|
||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief return the key of an object iterator
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
const typename object_t::key_type& key() const
|
||||
{
|
||||
JSON_ASSERT(m_object != nullptr);
|
||||
|
||||
if (JSON_HEDLEY_LIKELY(m_object->is_object()))
|
||||
{
|
||||
return m_it.object_iterator->first;
|
||||
}
|
||||
|
||||
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief return the value of an iterator
|
||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||
*/
|
||||
reference value() const
|
||||
{
|
||||
return operator*();
|
||||
}
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
/// associated JSON instance
|
||||
pointer m_object = nullptr;
|
||||
/// the actual iterator of the associated instance
|
||||
internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,242 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <iterator> // input_iterator_tag
|
||||
#include <string> // string, to_string
|
||||
#include <tuple> // tuple_size, get, tuple_element
|
||||
#include <utility> // move
|
||||
|
||||
#if JSON_HAS_RANGES
|
||||
#include <ranges> // enable_borrowed_range
|
||||
#endif
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename string_type>
|
||||
void int_to_string( string_type& target, std::size_t value )
|
||||
{
|
||||
// For ADL
|
||||
using std::to_string;
|
||||
target = to_string(value);
|
||||
}
|
||||
template<typename IteratorType> class iteration_proxy_value
|
||||
{
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = iteration_proxy_value;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
|
||||
|
||||
private:
|
||||
/// the iterator
|
||||
IteratorType anchor{};
|
||||
/// an index for arrays (used to create key names)
|
||||
std::size_t array_index = 0;
|
||||
/// last stringified array index
|
||||
mutable std::size_t array_index_last = 0;
|
||||
/// a string representation of the array index
|
||||
mutable string_type array_index_str = "0";
|
||||
/// an empty string (to return a reference for primitive values)
|
||||
string_type empty_str{};
|
||||
|
||||
public:
|
||||
explicit iteration_proxy_value() = default;
|
||||
explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
|
||||
noexcept(std::is_nothrow_move_constructible<IteratorType>::value
|
||||
&& std::is_nothrow_default_constructible<string_type>::value)
|
||||
: anchor(std::move(it))
|
||||
, array_index(array_index_)
|
||||
{}
|
||||
|
||||
iteration_proxy_value(iteration_proxy_value const&) = default;
|
||||
iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
|
||||
// older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
|
||||
iteration_proxy_value(iteration_proxy_value&&)
|
||||
noexcept(std::is_nothrow_move_constructible<IteratorType>::value
|
||||
&& std::is_nothrow_move_constructible<string_type>::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations)
|
||||
iteration_proxy_value& operator=(iteration_proxy_value&&)
|
||||
noexcept(std::is_nothrow_move_assignable<IteratorType>::value
|
||||
&& std::is_nothrow_move_assignable<string_type>::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations)
|
||||
~iteration_proxy_value() = default;
|
||||
|
||||
/// dereference operator (needed for range-based for)
|
||||
const iteration_proxy_value& operator*() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// increment operator (needed for range-based for)
|
||||
iteration_proxy_value& operator++()
|
||||
{
|
||||
++anchor;
|
||||
++array_index;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
|
||||
{
|
||||
auto tmp = iteration_proxy_value(anchor, array_index);
|
||||
++anchor;
|
||||
++array_index;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/// equality operator (needed for InputIterator)
|
||||
bool operator==(const iteration_proxy_value& o) const
|
||||
{
|
||||
return anchor == o.anchor;
|
||||
}
|
||||
|
||||
/// inequality operator (needed for range-based for)
|
||||
bool operator!=(const iteration_proxy_value& o) const
|
||||
{
|
||||
return anchor != o.anchor;
|
||||
}
|
||||
|
||||
/// return key of the iterator
|
||||
const string_type& key() const
|
||||
{
|
||||
JSON_ASSERT(anchor.m_object != nullptr);
|
||||
|
||||
switch (anchor.m_object->type())
|
||||
{
|
||||
// use integer array index as key
|
||||
case value_t::array:
|
||||
{
|
||||
if (array_index != array_index_last)
|
||||
{
|
||||
int_to_string( array_index_str, array_index );
|
||||
array_index_last = array_index;
|
||||
}
|
||||
return array_index_str;
|
||||
}
|
||||
|
||||
// use key from the object
|
||||
case value_t::object:
|
||||
return anchor.key();
|
||||
|
||||
// use an empty key for all primitive types
|
||||
case value_t::null:
|
||||
case value_t::string:
|
||||
case value_t::boolean:
|
||||
case value_t::number_integer:
|
||||
case value_t::number_unsigned:
|
||||
case value_t::number_float:
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return empty_str;
|
||||
}
|
||||
}
|
||||
|
||||
/// return value of the iterator
|
||||
typename IteratorType::reference value() const
|
||||
{
|
||||
return anchor.value();
|
||||
}
|
||||
};
|
||||
|
||||
/// proxy class for the items() function
|
||||
template<typename IteratorType> class iteration_proxy
|
||||
{
|
||||
private:
|
||||
/// the container to iterate
|
||||
typename IteratorType::pointer container = nullptr;
|
||||
|
||||
public:
|
||||
explicit iteration_proxy() = default;
|
||||
|
||||
/// construct iteration proxy from a container
|
||||
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
|
||||
: container(&cont) {}
|
||||
|
||||
iteration_proxy(iteration_proxy const&) = default;
|
||||
iteration_proxy& operator=(iteration_proxy const&) = default;
|
||||
iteration_proxy(iteration_proxy&&) noexcept = default;
|
||||
iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
|
||||
~iteration_proxy() = default;
|
||||
|
||||
/// return iterator begin (needed for range-based for)
|
||||
iteration_proxy_value<IteratorType> begin() const noexcept
|
||||
{
|
||||
return iteration_proxy_value<IteratorType>(container->begin());
|
||||
}
|
||||
|
||||
/// return iterator end (needed for range-based for)
|
||||
iteration_proxy_value<IteratorType> end() const noexcept
|
||||
{
|
||||
return iteration_proxy_value<IteratorType>(container->end());
|
||||
}
|
||||
};
|
||||
|
||||
// Structured Bindings Support
|
||||
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
||||
// And see https://github.com/nlohmann/json/pull/1391
|
||||
template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
|
||||
auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
|
||||
{
|
||||
return i.key();
|
||||
}
|
||||
// Structured Bindings Support
|
||||
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
||||
// And see https://github.com/nlohmann/json/pull/1391
|
||||
template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
|
||||
auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
|
||||
{
|
||||
return i.value();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
// The Addition to the STD Namespace is required to add
|
||||
// Structured Bindings Support to the iteration_proxy_value class
|
||||
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
||||
// And see https://github.com/nlohmann/json/pull/1391
|
||||
namespace std
|
||||
{
|
||||
|
||||
#if defined(__clang__)
|
||||
// Fix: https://github.com/nlohmann/json/issues/1401
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||
#endif
|
||||
template<typename IteratorType>
|
||||
class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> // NOLINT(cert-dcl58-cpp)
|
||||
: public std::integral_constant<std::size_t, 2> {};
|
||||
|
||||
template<std::size_t N, typename IteratorType>
|
||||
class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> // NOLINT(cert-dcl58-cpp)
|
||||
{
|
||||
public:
|
||||
using type = decltype(
|
||||
get<N>(std::declval <
|
||||
::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
|
||||
};
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace std
|
||||
|
||||
#if JSON_HAS_RANGES
|
||||
template <typename IteratorType>
|
||||
inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
|
||||
#endif
|
||||
@@ -1,61 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator> // random_access_iterator_tag
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/meta/void_t.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename It, typename = void>
|
||||
struct iterator_types {};
|
||||
|
||||
template<typename It>
|
||||
struct iterator_types <
|
||||
It,
|
||||
void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
|
||||
typename It::reference, typename It::iterator_category >>
|
||||
{
|
||||
using difference_type = typename It::difference_type;
|
||||
using value_type = typename It::value_type;
|
||||
using pointer = typename It::pointer;
|
||||
using reference = typename It::reference;
|
||||
using iterator_category = typename It::iterator_category;
|
||||
};
|
||||
|
||||
// This is required as some compilers implement std::iterator_traits in a way that
|
||||
// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
|
||||
template<typename T, typename = void>
|
||||
struct iterator_traits
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
|
||||
: iterator_types<T>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
|
||||
{
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,130 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // ptrdiff_t
|
||||
#include <iterator> // reverse_iterator
|
||||
#include <utility> // declval
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
//////////////////////
|
||||
// reverse_iterator //
|
||||
//////////////////////
|
||||
|
||||
/*!
|
||||
@brief a template for a reverse iterator class
|
||||
|
||||
@tparam Base the base iterator type to reverse. Valid types are @ref
|
||||
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
|
||||
create @ref const_reverse_iterator).
|
||||
|
||||
@requirement The class satisfies the following concept requirements:
|
||||
-
|
||||
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
|
||||
The iterator that can be moved can be moved in both directions (i.e.
|
||||
incremented and decremented).
|
||||
- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
|
||||
It is possible to write to the pointed-to element (only if @a Base is
|
||||
@ref iterator).
|
||||
|
||||
@since version 1.0.0
|
||||
*/
|
||||
template<typename Base>
|
||||
class json_reverse_iterator : public std::reverse_iterator<Base>
|
||||
{
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
/// shortcut to the reverse iterator adapter
|
||||
using base_iterator = std::reverse_iterator<Base>;
|
||||
/// the reference type for the pointed-to element
|
||||
using reference = typename Base::reference;
|
||||
|
||||
/// create reverse iterator from iterator
|
||||
explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
|
||||
: base_iterator(it) {}
|
||||
|
||||
/// create reverse iterator from base class
|
||||
explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
|
||||
|
||||
/// post-increment (it++)
|
||||
json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp)
|
||||
{
|
||||
return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
|
||||
}
|
||||
|
||||
/// pre-increment (++it)
|
||||
json_reverse_iterator& operator++()
|
||||
{
|
||||
return static_cast<json_reverse_iterator&>(base_iterator::operator++());
|
||||
}
|
||||
|
||||
/// post-decrement (it--)
|
||||
json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp)
|
||||
{
|
||||
return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
|
||||
}
|
||||
|
||||
/// pre-decrement (--it)
|
||||
json_reverse_iterator& operator--()
|
||||
{
|
||||
return static_cast<json_reverse_iterator&>(base_iterator::operator--());
|
||||
}
|
||||
|
||||
/// add to iterator
|
||||
json_reverse_iterator& operator+=(difference_type i)
|
||||
{
|
||||
return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
|
||||
}
|
||||
|
||||
/// add to iterator
|
||||
json_reverse_iterator operator+(difference_type i) const
|
||||
{
|
||||
return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
|
||||
}
|
||||
|
||||
/// subtract from iterator
|
||||
json_reverse_iterator operator-(difference_type i) const
|
||||
{
|
||||
return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
|
||||
}
|
||||
|
||||
/// return difference
|
||||
difference_type operator-(const json_reverse_iterator& other) const
|
||||
{
|
||||
return base_iterator(*this) - base_iterator(other);
|
||||
}
|
||||
|
||||
/// access to successor
|
||||
reference operator[](difference_type n) const
|
||||
{
|
||||
return *(this->operator+(n));
|
||||
}
|
||||
|
||||
/// return the key of an object iterator
|
||||
auto key() const -> decltype(std::declval<Base>().key())
|
||||
{
|
||||
auto it = --this->base();
|
||||
return it.key();
|
||||
}
|
||||
|
||||
/// return the value of an iterator
|
||||
reference value() const
|
||||
{
|
||||
auto it = --this->base();
|
||||
return it.operator * ();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,132 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // ptrdiff_t
|
||||
#include <limits> // numeric_limits
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/*
|
||||
@brief an iterator for primitive JSON types
|
||||
|
||||
This class models an iterator for primitive JSON types (boolean, number,
|
||||
string). It's only purpose is to allow the iterator/const_iterator classes
|
||||
to "iterate" over primitive values. Internally, the iterator is modeled by
|
||||
a `difference_type` variable. Value begin_value (`0`) models the begin,
|
||||
end_value (`1`) models past the end.
|
||||
*/
|
||||
class primitive_iterator_t
|
||||
{
|
||||
private:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
static constexpr difference_type begin_value = 0;
|
||||
static constexpr difference_type end_value = begin_value + 1;
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
/// iterator as signed integer type
|
||||
difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
|
||||
|
||||
public:
|
||||
constexpr difference_type get_value() const noexcept
|
||||
{
|
||||
return m_it;
|
||||
}
|
||||
|
||||
/// set iterator to a defined beginning
|
||||
void set_begin() noexcept
|
||||
{
|
||||
m_it = begin_value;
|
||||
}
|
||||
|
||||
/// set iterator to a defined past the end
|
||||
void set_end() noexcept
|
||||
{
|
||||
m_it = end_value;
|
||||
}
|
||||
|
||||
/// return whether the iterator can be dereferenced
|
||||
constexpr bool is_begin() const noexcept
|
||||
{
|
||||
return m_it == begin_value;
|
||||
}
|
||||
|
||||
/// return whether the iterator is at end
|
||||
constexpr bool is_end() const noexcept
|
||||
{
|
||||
return m_it == end_value;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
|
||||
{
|
||||
return lhs.m_it == rhs.m_it;
|
||||
}
|
||||
|
||||
friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
|
||||
{
|
||||
return lhs.m_it < rhs.m_it;
|
||||
}
|
||||
|
||||
primitive_iterator_t operator+(difference_type n) noexcept
|
||||
{
|
||||
auto result = *this;
|
||||
result += n;
|
||||
return result;
|
||||
}
|
||||
|
||||
friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
|
||||
{
|
||||
return lhs.m_it - rhs.m_it;
|
||||
}
|
||||
|
||||
primitive_iterator_t& operator++() noexcept
|
||||
{
|
||||
++m_it;
|
||||
return *this;
|
||||
}
|
||||
|
||||
primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp)
|
||||
{
|
||||
auto result = *this;
|
||||
++m_it;
|
||||
return result;
|
||||
}
|
||||
|
||||
primitive_iterator_t& operator--() noexcept
|
||||
{
|
||||
--m_it;
|
||||
return *this;
|
||||
}
|
||||
|
||||
primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp)
|
||||
{
|
||||
auto result = *this;
|
||||
--m_it;
|
||||
return result;
|
||||
}
|
||||
|
||||
primitive_iterator_t& operator+=(difference_type n) noexcept
|
||||
{
|
||||
m_it += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
primitive_iterator_t& operator-=(difference_type n) noexcept
|
||||
{
|
||||
m_it -= n;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,39 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits> // conditional, is_same
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/*!
|
||||
@brief Default base class of the @ref basic_json class.
|
||||
|
||||
So that the correct implementations of the copy / move ctors / assign operators
|
||||
of @ref basic_json do not require complex case distinctions
|
||||
(no base class / custom base class used as customization point),
|
||||
@ref basic_json always has a base class.
|
||||
By default, this class is used because it is empty and thus has no effect
|
||||
on the behavior of @ref basic_json.
|
||||
*/
|
||||
struct json_default_base {};
|
||||
|
||||
template<class T>
|
||||
using json_base_class = typename std::conditional <
|
||||
std::is_same<T, void>::value,
|
||||
json_default_base,
|
||||
T
|
||||
>::type;
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,988 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // all_of
|
||||
#include <cctype> // isdigit
|
||||
#include <cerrno> // errno, ERANGE
|
||||
#include <cstdlib> // strtoull
|
||||
#ifndef JSON_NO_IO
|
||||
#include <iosfwd> // ostream
|
||||
#endif // JSON_NO_IO
|
||||
#include <limits> // max
|
||||
#include <numeric> // accumulate
|
||||
#include <string> // string
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <nlohmann/detail/string_escape.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||
template<typename RefStringType>
|
||||
class json_pointer
|
||||
{
|
||||
// allow basic_json to access private members
|
||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||
friend class basic_json;
|
||||
|
||||
template<typename>
|
||||
friend class json_pointer;
|
||||
|
||||
template<typename T>
|
||||
struct string_t_helper
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||
struct string_t_helper<NLOHMANN_BASIC_JSON_TPL>
|
||||
{
|
||||
using type = StringType;
|
||||
};
|
||||
|
||||
public:
|
||||
// for backwards compatibility accept BasicJsonType
|
||||
using string_t = typename string_t_helper<RefStringType>::type;
|
||||
|
||||
/// @brief create JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
|
||||
explicit json_pointer(const string_t& s = "")
|
||||
: reference_tokens(split(s))
|
||||
{}
|
||||
|
||||
/// @brief return a string representation of the JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/to_string/
|
||||
string_t to_string() const
|
||||
{
|
||||
return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
|
||||
string_t{},
|
||||
[](const string_t& a, const string_t& b)
|
||||
{
|
||||
return detail::concat(a, '/', detail::escape(b));
|
||||
});
|
||||
}
|
||||
|
||||
/// @brief return a string representation of the JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
|
||||
operator string_t() const
|
||||
{
|
||||
return to_string();
|
||||
}
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
/// @brief write string representation of the JSON pointer to stream
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
|
||||
friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr)
|
||||
{
|
||||
o << ptr.to_string();
|
||||
return o;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @brief append another JSON pointer at the end of this JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
||||
json_pointer& operator/=(const json_pointer& ptr)
|
||||
{
|
||||
reference_tokens.insert(reference_tokens.end(),
|
||||
ptr.reference_tokens.begin(),
|
||||
ptr.reference_tokens.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief append an unescaped reference token at the end of this JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
||||
json_pointer& operator/=(string_t token)
|
||||
{
|
||||
push_back(std::move(token));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief append an array index at the end of this JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
||||
json_pointer& operator/=(std::size_t array_idx)
|
||||
{
|
||||
return *this /= std::to_string(array_idx);
|
||||
}
|
||||
|
||||
/// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
||||
friend json_pointer operator/(const json_pointer& lhs,
|
||||
const json_pointer& rhs)
|
||||
{
|
||||
return json_pointer(lhs) /= rhs;
|
||||
}
|
||||
|
||||
/// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
||||
friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
|
||||
{
|
||||
return json_pointer(lhs) /= std::move(token);
|
||||
}
|
||||
|
||||
/// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
||||
friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)
|
||||
{
|
||||
return json_pointer(lhs) /= array_idx;
|
||||
}
|
||||
|
||||
/// @brief returns the parent of this JSON pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/
|
||||
json_pointer parent_pointer() const
|
||||
{
|
||||
if (empty())
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
json_pointer res = *this;
|
||||
res.pop_back();
|
||||
return res;
|
||||
}
|
||||
|
||||
/// @brief remove last reference token
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/pop_back/
|
||||
void pop_back()
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
|
||||
}
|
||||
|
||||
reference_tokens.pop_back();
|
||||
}
|
||||
|
||||
/// @brief return last reference token
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/back/
|
||||
const string_t& back() const
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
|
||||
}
|
||||
|
||||
return reference_tokens.back();
|
||||
}
|
||||
|
||||
/// @brief append an unescaped token at the end of the reference pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/push_back/
|
||||
void push_back(const string_t& token)
|
||||
{
|
||||
reference_tokens.push_back(token);
|
||||
}
|
||||
|
||||
/// @brief append an unescaped token at the end of the reference pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/push_back/
|
||||
void push_back(string_t&& token)
|
||||
{
|
||||
reference_tokens.push_back(std::move(token));
|
||||
}
|
||||
|
||||
/// @brief return whether pointer points to the root document
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/empty/
|
||||
bool empty() const noexcept
|
||||
{
|
||||
return reference_tokens.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@param[in] s reference token to be converted into an array index
|
||||
|
||||
@return integer representation of @a s
|
||||
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if an array index begins not with a digit
|
||||
@throw out_of_range.404 if string @a s could not be converted to an integer
|
||||
@throw out_of_range.410 if an array index exceeds size_type
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
static typename BasicJsonType::size_type array_index(const string_t& s)
|
||||
{
|
||||
using size_type = typename BasicJsonType::size_type;
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
|
||||
}
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
|
||||
}
|
||||
|
||||
const char* p = s.c_str();
|
||||
char* p_end = nullptr;
|
||||
errno = 0; // strtoull doesn't reset errno
|
||||
const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
|
||||
if (p == p_end // invalid input or empty string
|
||||
|| errno == ERANGE // out of range
|
||||
|| JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
|
||||
}
|
||||
|
||||
// only triggered on special platforms (like 32bit), see also
|
||||
// https://github.com/nlohmann/json/pull/2203
|
||||
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
return static_cast<size_type>(res);
|
||||
}
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
json_pointer top() const
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
||||
{
|
||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
|
||||
}
|
||||
|
||||
json_pointer result = *this;
|
||||
result.reference_tokens = {reference_tokens[0]};
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief create and return a reference to the pointed to value
|
||||
|
||||
@complexity Linear in the number of reference tokens.
|
||||
|
||||
@throw parse_error.109 if array index is not a number
|
||||
@throw type_error.313 if value cannot be unflattened
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
BasicJsonType& get_and_create(BasicJsonType& j) const
|
||||
{
|
||||
auto* result = &j;
|
||||
|
||||
// in case no reference tokens exist, return a reference to the JSON value
|
||||
// j which will be overwritten by a primitive value
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (result->type())
|
||||
{
|
||||
case detail::value_t::null:
|
||||
{
|
||||
if (reference_token == "0")
|
||||
{
|
||||
// start a new array if reference token is 0
|
||||
result = &result->operator[](0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// start a new object otherwise
|
||||
result = &result->operator[](reference_token);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// create an entry in the object
|
||||
result = &result->operator[](reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::array:
|
||||
{
|
||||
// create an entry in the array
|
||||
result = &result->operator[](array_index<BasicJsonType>(reference_token));
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
The following code is only reached if there exists a reference
|
||||
token _and_ the current value is primitive. In this case, we have
|
||||
an error situation, because primitive values may only occur as
|
||||
single value; that is, with an empty list of reference tokens.
|
||||
*/
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
|
||||
}
|
||||
}
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief return a reference to the pointed to value
|
||||
|
||||
@note This version does not throw if a value is not present, but tries to
|
||||
create nested values instead. For instance, calling this function
|
||||
with pointer `"/this/that"` on a null value is equivalent to calling
|
||||
`operator[]("this").operator[]("that")` on that value, effectively
|
||||
changing the null value to an object.
|
||||
|
||||
@param[in] ptr a JSON value
|
||||
|
||||
@return reference to the JSON value pointed to by the JSON pointer
|
||||
|
||||
@complexity Linear in the length of the JSON pointer.
|
||||
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if an array index was not a number
|
||||
@throw out_of_range.404 if the JSON pointer can not be resolved
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
BasicJsonType& get_unchecked(BasicJsonType* ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
// convert null values to arrays or objects before continuing
|
||||
if (ptr->is_null())
|
||||
{
|
||||
// check if reference token is a number
|
||||
const bool nums =
|
||||
std::all_of(reference_token.begin(), reference_token.end(),
|
||||
[](const unsigned char x)
|
||||
{
|
||||
return std::isdigit(x);
|
||||
});
|
||||
|
||||
// change value to array for numbers or "-" or to object otherwise
|
||||
*ptr = (nums || reference_token == "-")
|
||||
? detail::value_t::array
|
||||
: detail::value_t::object;
|
||||
}
|
||||
|
||||
switch (ptr->type())
|
||||
{
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// use unchecked object access
|
||||
ptr = &ptr->operator[](reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (reference_token == "-")
|
||||
{
|
||||
// explicitly treat "-" as index beyond the end
|
||||
ptr = &ptr->operator[](ptr->m_data.m_value.array->size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert array index to number; unchecked access
|
||||
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
|
||||
}
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if an array index was not a number
|
||||
@throw out_of_range.402 if the array index '-' is used
|
||||
@throw out_of_range.404 if the JSON pointer can not be resolved
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
BasicJsonType& get_checked(BasicJsonType* ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (ptr->type())
|
||||
{
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// note: at performs range check
|
||||
ptr = &ptr->at(reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||
{
|
||||
// "-" always fails the range check
|
||||
JSON_THROW(detail::out_of_range::create(402, detail::concat(
|
||||
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
|
||||
") is out of range"), ptr));
|
||||
}
|
||||
|
||||
// note: at performs range check
|
||||
ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
|
||||
}
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief return a const reference to the pointed to value
|
||||
|
||||
@param[in] ptr a JSON value
|
||||
|
||||
@return const reference to the JSON value pointed to by the JSON
|
||||
pointer
|
||||
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if an array index was not a number
|
||||
@throw out_of_range.402 if the array index '-' is used
|
||||
@throw out_of_range.404 if the JSON pointer can not be resolved
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (ptr->type())
|
||||
{
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// use unchecked object access
|
||||
ptr = &ptr->operator[](reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||
{
|
||||
// "-" cannot be used for const access
|
||||
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
|
||||
}
|
||||
|
||||
// use unchecked array access
|
||||
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
|
||||
}
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if an array index was not a number
|
||||
@throw out_of_range.402 if the array index '-' is used
|
||||
@throw out_of_range.404 if the JSON pointer can not be resolved
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
const BasicJsonType& get_checked(const BasicJsonType* ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (ptr->type())
|
||||
{
|
||||
case detail::value_t::object:
|
||||
{
|
||||
// note: at performs range check
|
||||
ptr = &ptr->at(reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||
{
|
||||
// "-" always fails the range check
|
||||
JSON_THROW(detail::out_of_range::create(402, detail::concat(
|
||||
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
|
||||
") is out of range"), ptr));
|
||||
}
|
||||
|
||||
// note: at performs range check
|
||||
ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
|
||||
}
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
@throw parse_error.106 if an array index begins with '0'
|
||||
@throw parse_error.109 if an array index was not a number
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
bool contains(const BasicJsonType* ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (ptr->type())
|
||||
{
|
||||
case detail::value_t::object:
|
||||
{
|
||||
if (!ptr->contains(reference_token))
|
||||
{
|
||||
// we did not find the key in the object
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr = &ptr->operator[](reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||
{
|
||||
// "-" always fails the range check
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
|
||||
{
|
||||
// invalid char
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
|
||||
{
|
||||
// first char should be between '1' and '9'
|
||||
return false;
|
||||
}
|
||||
for (std::size_t i = 1; i < reference_token.size(); i++)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
|
||||
{
|
||||
// other char should be between '0' and '9'
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto idx = array_index<BasicJsonType>(reference_token);
|
||||
if (idx >= ptr->size())
|
||||
{
|
||||
// index out of range
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr = &ptr->operator[](idx);
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// we do not expect primitive values if there is still a
|
||||
// reference token to process
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no reference token left means we found a primitive value
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief split the string input to reference tokens
|
||||
|
||||
@note This function is only called by the json_pointer constructor.
|
||||
All exceptions below are documented there.
|
||||
|
||||
@throw parse_error.107 if the pointer is not empty or begins with '/'
|
||||
@throw parse_error.108 if character '~' is not followed by '0' or '1'
|
||||
*/
|
||||
static std::vector<string_t> split(const string_t& reference_string)
|
||||
{
|
||||
std::vector<string_t> result;
|
||||
|
||||
// special case: empty reference string -> no reference tokens
|
||||
if (reference_string.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// check if nonempty reference string begins with slash
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
|
||||
}
|
||||
|
||||
// extract the reference tokens:
|
||||
// - slash: position of the last read slash (or end of string)
|
||||
// - start: position after the previous slash
|
||||
for (
|
||||
// search for the first slash after the first character
|
||||
std::size_t slash = reference_string.find_first_of('/', 1),
|
||||
// set the beginning of the first reference token
|
||||
start = 1;
|
||||
// we can stop if start == 0 (if slash == string_t::npos)
|
||||
start != 0;
|
||||
// set the beginning of the next reference token
|
||||
// (will eventually be 0 if slash == string_t::npos)
|
||||
start = (slash == string_t::npos) ? 0 : slash + 1,
|
||||
// find next slash
|
||||
slash = reference_string.find_first_of('/', start))
|
||||
{
|
||||
// use the text between the beginning of the reference token
|
||||
// (start) and the last slash (slash).
|
||||
auto reference_token = reference_string.substr(start, slash - start);
|
||||
|
||||
// check reference tokens are properly escaped
|
||||
for (std::size_t pos = reference_token.find_first_of('~');
|
||||
pos != string_t::npos;
|
||||
pos = reference_token.find_first_of('~', pos + 1))
|
||||
{
|
||||
JSON_ASSERT(reference_token[pos] == '~');
|
||||
|
||||
// ~ must be followed by 0 or 1
|
||||
if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
|
||||
(reference_token[pos + 1] != '0' &&
|
||||
reference_token[pos + 1] != '1')))
|
||||
{
|
||||
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
// finally, store the reference token
|
||||
detail::unescape(reference_token);
|
||||
result.push_back(reference_token);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@param[in] reference_string the reference string to the current value
|
||||
@param[in] value the value to consider
|
||||
@param[in,out] result the result object to insert values to
|
||||
|
||||
@note Empty objects or arrays are flattened to `null`.
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
static void flatten(const string_t& reference_string,
|
||||
const BasicJsonType& value,
|
||||
BasicJsonType& result)
|
||||
{
|
||||
switch (value.type())
|
||||
{
|
||||
case detail::value_t::array:
|
||||
{
|
||||
if (value.m_data.m_value.array->empty())
|
||||
{
|
||||
// flatten empty array as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// iterate array and use index as reference string
|
||||
for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)
|
||||
{
|
||||
flatten(detail::concat(reference_string, '/', std::to_string(i)),
|
||||
value.m_data.m_value.array->operator[](i), result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::object:
|
||||
{
|
||||
if (value.m_data.m_value.object->empty())
|
||||
{
|
||||
// flatten empty object as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// iterate object and use keys as reference string
|
||||
for (const auto& element : *value.m_data.m_value.object)
|
||||
{
|
||||
flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case detail::value_t::null:
|
||||
case detail::value_t::string:
|
||||
case detail::value_t::boolean:
|
||||
case detail::value_t::number_integer:
|
||||
case detail::value_t::number_unsigned:
|
||||
case detail::value_t::number_float:
|
||||
case detail::value_t::binary:
|
||||
case detail::value_t::discarded:
|
||||
default:
|
||||
{
|
||||
// add primitive value with its reference string
|
||||
result[reference_string] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@param[in] value flattened JSON
|
||||
|
||||
@return unflattened JSON
|
||||
|
||||
@throw parse_error.109 if array index is not a number
|
||||
@throw type_error.314 if value is not an object
|
||||
@throw type_error.315 if object values are not primitive
|
||||
@throw type_error.313 if value cannot be unflattened
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
static BasicJsonType
|
||||
unflatten(const BasicJsonType& value)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
|
||||
{
|
||||
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
|
||||
}
|
||||
|
||||
BasicJsonType result;
|
||||
|
||||
// iterate the JSON object values
|
||||
for (const auto& element : *value.m_data.m_value.object)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
|
||||
{
|
||||
JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
|
||||
}
|
||||
|
||||
// assign value to reference pointed to by JSON pointer; Note that if
|
||||
// the JSON pointer is "" (i.e., points to the whole value), function
|
||||
// get_and_create returns a reference to result itself. An assignment
|
||||
// will then create a primitive value.
|
||||
json_pointer(element.first).get_and_create(result) = element.second;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// can't use conversion operator because of ambiguity
|
||||
json_pointer<string_t> convert() const&
|
||||
{
|
||||
json_pointer<string_t> result;
|
||||
result.reference_tokens = reference_tokens;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_pointer<string_t> convert()&&
|
||||
{
|
||||
json_pointer<string_t> result;
|
||||
result.reference_tokens = std::move(reference_tokens);
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
/// @brief compares two JSON pointers for equality
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
|
||||
template<typename RefStringTypeRhs>
|
||||
bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept
|
||||
{
|
||||
return reference_tokens == rhs.reference_tokens;
|
||||
}
|
||||
|
||||
/// @brief compares JSON pointer and string for equality
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))
|
||||
bool operator==(const string_t& rhs) const
|
||||
{
|
||||
return *this == json_pointer(rhs);
|
||||
}
|
||||
|
||||
/// @brief 3-way compares two JSON pointers
|
||||
template<typename RefStringTypeRhs>
|
||||
std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD*
|
||||
{
|
||||
return reference_tokens <=> rhs.reference_tokens; // *NOPAD*
|
||||
}
|
||||
#else
|
||||
/// @brief compares two JSON pointers for equality
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs) noexcept;
|
||||
|
||||
/// @brief compares JSON pointer and string for equality
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
|
||||
template<typename RefStringTypeLhs, typename StringType>
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const StringType& rhs);
|
||||
|
||||
/// @brief compares string and JSON pointer for equality
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
|
||||
template<typename RefStringTypeRhs, typename StringType>
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
friend bool operator==(const StringType& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs);
|
||||
|
||||
/// @brief compares two JSON pointers for inequality
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs) noexcept;
|
||||
|
||||
/// @brief compares JSON pointer and string for inequality
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
|
||||
template<typename RefStringTypeLhs, typename StringType>
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const StringType& rhs);
|
||||
|
||||
/// @brief compares string and JSON pointer for inequality
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
|
||||
template<typename RefStringTypeRhs, typename StringType>
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
friend bool operator!=(const StringType& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs);
|
||||
|
||||
/// @brief compares two JSON pointer for less-than
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
// NOLINTNEXTLINE(readability-redundant-declaration)
|
||||
friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs) noexcept;
|
||||
#endif
|
||||
|
||||
private:
|
||||
/// the reference tokens
|
||||
std::vector<string_t> reference_tokens;
|
||||
};
|
||||
|
||||
#if !JSON_HAS_THREE_WAY_COMPARISON
|
||||
// functions cannot be defined inside class due to ODR violations
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs) noexcept
|
||||
{
|
||||
return lhs.reference_tokens == rhs.reference_tokens;
|
||||
}
|
||||
|
||||
template<typename RefStringTypeLhs,
|
||||
typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
|
||||
inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const StringType& rhs)
|
||||
{
|
||||
return lhs == json_pointer<RefStringTypeLhs>(rhs);
|
||||
}
|
||||
|
||||
template<typename RefStringTypeRhs,
|
||||
typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
|
||||
inline bool operator==(const StringType& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs)
|
||||
{
|
||||
return json_pointer<RefStringTypeRhs>(lhs) == rhs;
|
||||
}
|
||||
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename RefStringTypeLhs,
|
||||
typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
|
||||
inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const StringType& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename RefStringTypeRhs,
|
||||
typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
|
||||
inline bool operator!=(const StringType& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename RefStringTypeLhs, typename RefStringTypeRhs>
|
||||
inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
const json_pointer<RefStringTypeRhs>& rhs) noexcept
|
||||
{
|
||||
return lhs.reference_tokens < rhs.reference_tokens;
|
||||
}
|
||||
#endif
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,78 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class json_ref
|
||||
{
|
||||
public:
|
||||
using value_type = BasicJsonType;
|
||||
|
||||
json_ref(value_type&& value)
|
||||
: owned_value(std::move(value))
|
||||
{}
|
||||
|
||||
json_ref(const value_type& value)
|
||||
: value_ref(&value)
|
||||
{}
|
||||
|
||||
json_ref(std::initializer_list<json_ref> init)
|
||||
: owned_value(init)
|
||||
{}
|
||||
|
||||
template <
|
||||
class... Args,
|
||||
enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >
|
||||
json_ref(Args && ... args)
|
||||
: owned_value(std::forward<Args>(args)...)
|
||||
{}
|
||||
|
||||
// class should be movable only
|
||||
json_ref(json_ref&&) noexcept = default;
|
||||
json_ref(const json_ref&) = delete;
|
||||
json_ref& operator=(const json_ref&) = delete;
|
||||
json_ref& operator=(json_ref&&) = delete;
|
||||
~json_ref() = default;
|
||||
|
||||
value_type moved_or_copied() const
|
||||
{
|
||||
if (value_ref == nullptr)
|
||||
{
|
||||
return std::move(owned_value);
|
||||
}
|
||||
return *value_ref;
|
||||
}
|
||||
|
||||
value_type const& operator*() const
|
||||
{
|
||||
return value_ref ? *value_ref : owned_value;
|
||||
}
|
||||
|
||||
value_type const* operator->() const
|
||||
{
|
||||
return &** this;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable value_type owned_value = nullptr;
|
||||
value_type const* value_ref = nullptr;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,482 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility> // declval, pair
|
||||
#include <nlohmann/detail/meta/detected.hpp>
|
||||
#include <nlohmann/thirdparty/hedley/hedley.hpp>
|
||||
|
||||
// This file contains all internal macro definitions (except those affecting ABI)
|
||||
// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
// exclude unsupported compilers
|
||||
#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
|
||||
#if defined(__clang__)
|
||||
#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
|
||||
#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
|
||||
#endif
|
||||
#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
|
||||
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
|
||||
#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// C++ language standard detection
|
||||
// if the user manually specified the used c++ version this is skipped
|
||||
#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
|
||||
#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
|
||||
#define JSON_HAS_CPP_20
|
||||
#define JSON_HAS_CPP_17
|
||||
#define JSON_HAS_CPP_14
|
||||
#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
|
||||
#define JSON_HAS_CPP_17
|
||||
#define JSON_HAS_CPP_14
|
||||
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
|
||||
#define JSON_HAS_CPP_14
|
||||
#endif
|
||||
// the cpp 11 flag is always specified because it is the minimal required version
|
||||
#define JSON_HAS_CPP_11
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<version>)
|
||||
#include <version>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
#if defined(__cpp_lib_filesystem)
|
||||
#define JSON_HAS_FILESYSTEM 1
|
||||
#elif defined(__cpp_lib_experimental_filesystem)
|
||||
#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
|
||||
#elif !defined(__has_include)
|
||||
#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
|
||||
#elif __has_include(<filesystem>)
|
||||
#define JSON_HAS_FILESYSTEM 1
|
||||
#elif __has_include(<experimental/filesystem>)
|
||||
#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
|
||||
#endif
|
||||
|
||||
// std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/
|
||||
#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8
|
||||
#undef JSON_HAS_FILESYSTEM
|
||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#endif
|
||||
|
||||
// no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support
|
||||
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
|
||||
#undef JSON_HAS_FILESYSTEM
|
||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#endif
|
||||
|
||||
// no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support
|
||||
#if defined(__clang_major__) && __clang_major__ < 7
|
||||
#undef JSON_HAS_FILESYSTEM
|
||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#endif
|
||||
|
||||
// no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1914
|
||||
#undef JSON_HAS_FILESYSTEM
|
||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#endif
|
||||
|
||||
// no filesystem support before iOS 13
|
||||
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000
|
||||
#undef JSON_HAS_FILESYSTEM
|
||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#endif
|
||||
|
||||
// no filesystem support before macOS Catalina
|
||||
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
|
||||
#undef JSON_HAS_FILESYSTEM
|
||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_FILESYSTEM
|
||||
#define JSON_HAS_FILESYSTEM 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_THREE_WAY_COMPARISON
|
||||
#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
|
||||
&& defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
|
||||
#define JSON_HAS_THREE_WAY_COMPARISON 1
|
||||
#else
|
||||
#define JSON_HAS_THREE_WAY_COMPARISON 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_RANGES
|
||||
// ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
|
||||
#if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
|
||||
#define JSON_HAS_RANGES 0
|
||||
#elif defined(__cpp_lib_ranges)
|
||||
#define JSON_HAS_RANGES 1
|
||||
#else
|
||||
#define JSON_HAS_RANGES 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_STATIC_RTTI
|
||||
#if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0
|
||||
#define JSON_HAS_STATIC_RTTI 1
|
||||
#else
|
||||
#define JSON_HAS_STATIC_RTTI 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
#define JSON_INLINE_VARIABLE inline
|
||||
#else
|
||||
#define JSON_INLINE_VARIABLE
|
||||
#endif
|
||||
|
||||
#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
|
||||
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
|
||||
#else
|
||||
#define JSON_NO_UNIQUE_ADDRESS
|
||||
#endif
|
||||
|
||||
// disable documentation warnings on clang
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdocumentation"
|
||||
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
||||
#endif
|
||||
|
||||
// allow disabling exceptions
|
||||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
|
||||
#define JSON_THROW(exception) throw exception
|
||||
#define JSON_TRY try
|
||||
#define JSON_CATCH(exception) catch(exception)
|
||||
#define JSON_INTERNAL_CATCH(exception) catch(exception)
|
||||
#else
|
||||
#include <cstdlib>
|
||||
#define JSON_THROW(exception) std::abort()
|
||||
#define JSON_TRY if(true)
|
||||
#define JSON_CATCH(exception) if(false)
|
||||
#define JSON_INTERNAL_CATCH(exception) if(false)
|
||||
#endif
|
||||
|
||||
// override exception macros
|
||||
#if defined(JSON_THROW_USER)
|
||||
#undef JSON_THROW
|
||||
#define JSON_THROW JSON_THROW_USER
|
||||
#endif
|
||||
#if defined(JSON_TRY_USER)
|
||||
#undef JSON_TRY
|
||||
#define JSON_TRY JSON_TRY_USER
|
||||
#endif
|
||||
#if defined(JSON_CATCH_USER)
|
||||
#undef JSON_CATCH
|
||||
#define JSON_CATCH JSON_CATCH_USER
|
||||
#undef JSON_INTERNAL_CATCH
|
||||
#define JSON_INTERNAL_CATCH JSON_CATCH_USER
|
||||
#endif
|
||||
#if defined(JSON_INTERNAL_CATCH_USER)
|
||||
#undef JSON_INTERNAL_CATCH
|
||||
#define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
|
||||
#endif
|
||||
|
||||
// allow overriding assert
|
||||
#if !defined(JSON_ASSERT)
|
||||
#include <cassert> // assert
|
||||
#define JSON_ASSERT(x) assert(x)
|
||||
#endif
|
||||
|
||||
// allow to access some private functions (needed by the test suite)
|
||||
#if defined(JSON_TESTS_PRIVATE)
|
||||
#define JSON_PRIVATE_UNLESS_TESTED public
|
||||
#else
|
||||
#define JSON_PRIVATE_UNLESS_TESTED private
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@brief macro to briefly define a mapping between an enum and JSON
|
||||
@def NLOHMANN_JSON_SERIALIZE_ENUM
|
||||
@since version 3.4.0
|
||||
*/
|
||||
#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \
|
||||
template<typename BasicJsonType> \
|
||||
inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \
|
||||
{ \
|
||||
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
||||
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
||||
auto it = std::find_if(std::begin(m), std::end(m), \
|
||||
[e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
||||
{ \
|
||||
return ej_pair.first == e; \
|
||||
}); \
|
||||
j = ((it != std::end(m)) ? it : std::begin(m))->second; \
|
||||
} \
|
||||
template<typename BasicJsonType> \
|
||||
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
|
||||
{ \
|
||||
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
||||
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
||||
auto it = std::find_if(std::begin(m), std::end(m), \
|
||||
[&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
||||
{ \
|
||||
return ej_pair.second == j; \
|
||||
}); \
|
||||
e = ((it != std::end(m)) ? it : std::begin(m))->first; \
|
||||
}
|
||||
|
||||
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
||||
// may be removed in the future once the class is split.
|
||||
|
||||
#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
|
||||
template<template<typename, typename, typename...> class ObjectType, \
|
||||
template<typename, typename...> class ArrayType, \
|
||||
class StringType, class BooleanType, class NumberIntegerType, \
|
||||
class NumberUnsignedType, class NumberFloatType, \
|
||||
template<typename> class AllocatorType, \
|
||||
template<typename, typename = void> class JSONSerializer, \
|
||||
class BinaryType, \
|
||||
class CustomBaseClass>
|
||||
|
||||
#define NLOHMANN_BASIC_JSON_TPL \
|
||||
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
|
||||
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
|
||||
AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>
|
||||
|
||||
// Macros to simplify conversion from/to types
|
||||
|
||||
#define NLOHMANN_JSON_EXPAND( x ) x
|
||||
#define NLOHMANN_JSON_GET_MACRO(_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, NAME,...) NAME
|
||||
#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
||||
NLOHMANN_JSON_PASTE64, \
|
||||
NLOHMANN_JSON_PASTE63, \
|
||||
NLOHMANN_JSON_PASTE62, \
|
||||
NLOHMANN_JSON_PASTE61, \
|
||||
NLOHMANN_JSON_PASTE60, \
|
||||
NLOHMANN_JSON_PASTE59, \
|
||||
NLOHMANN_JSON_PASTE58, \
|
||||
NLOHMANN_JSON_PASTE57, \
|
||||
NLOHMANN_JSON_PASTE56, \
|
||||
NLOHMANN_JSON_PASTE55, \
|
||||
NLOHMANN_JSON_PASTE54, \
|
||||
NLOHMANN_JSON_PASTE53, \
|
||||
NLOHMANN_JSON_PASTE52, \
|
||||
NLOHMANN_JSON_PASTE51, \
|
||||
NLOHMANN_JSON_PASTE50, \
|
||||
NLOHMANN_JSON_PASTE49, \
|
||||
NLOHMANN_JSON_PASTE48, \
|
||||
NLOHMANN_JSON_PASTE47, \
|
||||
NLOHMANN_JSON_PASTE46, \
|
||||
NLOHMANN_JSON_PASTE45, \
|
||||
NLOHMANN_JSON_PASTE44, \
|
||||
NLOHMANN_JSON_PASTE43, \
|
||||
NLOHMANN_JSON_PASTE42, \
|
||||
NLOHMANN_JSON_PASTE41, \
|
||||
NLOHMANN_JSON_PASTE40, \
|
||||
NLOHMANN_JSON_PASTE39, \
|
||||
NLOHMANN_JSON_PASTE38, \
|
||||
NLOHMANN_JSON_PASTE37, \
|
||||
NLOHMANN_JSON_PASTE36, \
|
||||
NLOHMANN_JSON_PASTE35, \
|
||||
NLOHMANN_JSON_PASTE34, \
|
||||
NLOHMANN_JSON_PASTE33, \
|
||||
NLOHMANN_JSON_PASTE32, \
|
||||
NLOHMANN_JSON_PASTE31, \
|
||||
NLOHMANN_JSON_PASTE30, \
|
||||
NLOHMANN_JSON_PASTE29, \
|
||||
NLOHMANN_JSON_PASTE28, \
|
||||
NLOHMANN_JSON_PASTE27, \
|
||||
NLOHMANN_JSON_PASTE26, \
|
||||
NLOHMANN_JSON_PASTE25, \
|
||||
NLOHMANN_JSON_PASTE24, \
|
||||
NLOHMANN_JSON_PASTE23, \
|
||||
NLOHMANN_JSON_PASTE22, \
|
||||
NLOHMANN_JSON_PASTE21, \
|
||||
NLOHMANN_JSON_PASTE20, \
|
||||
NLOHMANN_JSON_PASTE19, \
|
||||
NLOHMANN_JSON_PASTE18, \
|
||||
NLOHMANN_JSON_PASTE17, \
|
||||
NLOHMANN_JSON_PASTE16, \
|
||||
NLOHMANN_JSON_PASTE15, \
|
||||
NLOHMANN_JSON_PASTE14, \
|
||||
NLOHMANN_JSON_PASTE13, \
|
||||
NLOHMANN_JSON_PASTE12, \
|
||||
NLOHMANN_JSON_PASTE11, \
|
||||
NLOHMANN_JSON_PASTE10, \
|
||||
NLOHMANN_JSON_PASTE9, \
|
||||
NLOHMANN_JSON_PASTE8, \
|
||||
NLOHMANN_JSON_PASTE7, \
|
||||
NLOHMANN_JSON_PASTE6, \
|
||||
NLOHMANN_JSON_PASTE5, \
|
||||
NLOHMANN_JSON_PASTE4, \
|
||||
NLOHMANN_JSON_PASTE3, \
|
||||
NLOHMANN_JSON_PASTE2, \
|
||||
NLOHMANN_JSON_PASTE1)(__VA_ARGS__))
|
||||
#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)
|
||||
#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)
|
||||
#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)
|
||||
#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)
|
||||
#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)
|
||||
#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)
|
||||
#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
|
||||
#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
|
||||
#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)
|
||||
#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
|
||||
#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
|
||||
#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)
|
||||
#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)
|
||||
#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)
|
||||
#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
|
||||
#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)
|
||||
#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
|
||||
#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)
|
||||
#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)
|
||||
#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
|
||||
#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)
|
||||
#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)
|
||||
#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)
|
||||
#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)
|
||||
#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)
|
||||
#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)
|
||||
#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)
|
||||
#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)
|
||||
#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)
|
||||
#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)
|
||||
#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)
|
||||
#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)
|
||||
#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)
|
||||
#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)
|
||||
#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)
|
||||
#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)
|
||||
#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)
|
||||
#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)
|
||||
#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)
|
||||
#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)
|
||||
#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)
|
||||
#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)
|
||||
#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)
|
||||
#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)
|
||||
#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)
|
||||
#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)
|
||||
#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)
|
||||
#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)
|
||||
#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)
|
||||
#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)
|
||||
#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)
|
||||
#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)
|
||||
#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)
|
||||
#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)
|
||||
#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)
|
||||
#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)
|
||||
#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)
|
||||
#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)
|
||||
#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)
|
||||
#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)
|
||||
#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)
|
||||
#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)
|
||||
#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)
|
||||
|
||||
#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
|
||||
#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
|
||||
#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
|
||||
|
||||
/*!
|
||||
@brief macro
|
||||
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
|
||||
@since version 3.9.0
|
||||
*/
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
|
||||
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
|
||||
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
|
||||
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
|
||||
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
|
||||
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
|
||||
|
||||
/*!
|
||||
@brief macro
|
||||
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
|
||||
@since version 3.9.0
|
||||
*/
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
|
||||
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
|
||||
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
|
||||
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
|
||||
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
|
||||
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
||||
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
|
||||
|
||||
// inspired from https://stackoverflow.com/a/26745591
|
||||
// allows to call any std function as if (e.g. with begin):
|
||||
// using std::begin; begin(x);
|
||||
//
|
||||
// it allows using the detected idiom to retrieve the return type
|
||||
// of such an expression
|
||||
#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \
|
||||
namespace detail { \
|
||||
using std::std_name; \
|
||||
\
|
||||
template<typename... T> \
|
||||
using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
|
||||
} \
|
||||
\
|
||||
namespace detail2 { \
|
||||
struct std_name##_tag \
|
||||
{ \
|
||||
}; \
|
||||
\
|
||||
template<typename... T> \
|
||||
std_name##_tag std_name(T&&...); \
|
||||
\
|
||||
template<typename... T> \
|
||||
using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
|
||||
\
|
||||
template<typename... T> \
|
||||
struct would_call_std_##std_name \
|
||||
{ \
|
||||
static constexpr auto const value = ::nlohmann::detail:: \
|
||||
is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \
|
||||
}; \
|
||||
} /* namespace detail2 */ \
|
||||
\
|
||||
template<typename... T> \
|
||||
struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...> \
|
||||
{ \
|
||||
}
|
||||
|
||||
#ifndef JSON_USE_IMPLICIT_CONVERSIONS
|
||||
#define JSON_USE_IMPLICIT_CONVERSIONS 1
|
||||
#endif
|
||||
|
||||
#if JSON_USE_IMPLICIT_CONVERSIONS
|
||||
#define JSON_EXPLICIT
|
||||
#else
|
||||
#define JSON_EXPLICIT explicit
|
||||
#endif
|
||||
|
||||
#ifndef JSON_DISABLE_ENUM_SERIALIZATION
|
||||
#define JSON_DISABLE_ENUM_SERIALIZATION 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_USE_GLOBAL_UDLS
|
||||
#define JSON_USE_GLOBAL_UDLS 1
|
||||
#endif
|
||||
@@ -1,45 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
// restore clang diagnostic settings
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
// clean up
|
||||
#undef JSON_ASSERT
|
||||
#undef JSON_INTERNAL_CATCH
|
||||
#undef JSON_THROW
|
||||
#undef JSON_PRIVATE_UNLESS_TESTED
|
||||
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||
#undef NLOHMANN_BASIC_JSON_TPL
|
||||
#undef JSON_EXPLICIT
|
||||
#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL
|
||||
#undef JSON_INLINE_VARIABLE
|
||||
#undef JSON_NO_UNIQUE_ADDRESS
|
||||
#undef JSON_DISABLE_ENUM_SERIALIZATION
|
||||
#undef JSON_USE_GLOBAL_UDLS
|
||||
|
||||
#ifndef JSON_TEST_KEEP_MACROS
|
||||
#undef JSON_CATCH
|
||||
#undef JSON_TRY
|
||||
#undef JSON_HAS_CPP_11
|
||||
#undef JSON_HAS_CPP_14
|
||||
#undef JSON_HAS_CPP_17
|
||||
#undef JSON_HAS_CPP_20
|
||||
#undef JSON_HAS_FILESYSTEM
|
||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#undef JSON_HAS_THREE_WAY_COMPARISON
|
||||
#undef JSON_HAS_RANGES
|
||||
#undef JSON_HAS_STATIC_RTTI
|
||||
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#endif
|
||||
|
||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||
@@ -1,17 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,17 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,171 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-FileCopyrightText: 2018 The Abseil Authors
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
|
||||
#include <utility> // index_sequence, make_index_sequence, index_sequence_for
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
#ifdef JSON_HAS_CPP_14
|
||||
|
||||
// the following utilities are natively available in C++14
|
||||
using std::enable_if_t;
|
||||
using std::index_sequence;
|
||||
using std::make_index_sequence;
|
||||
using std::index_sequence_for;
|
||||
|
||||
#else
|
||||
|
||||
// alias templates to reduce boilerplate
|
||||
template<bool B, typename T = void>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h
|
||||
// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.
|
||||
|
||||
//// START OF CODE FROM GOOGLE ABSEIL
|
||||
|
||||
// integer_sequence
|
||||
//
|
||||
// Class template representing a compile-time integer sequence. An instantiation
|
||||
// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
|
||||
// type through its template arguments (which is a common need when
|
||||
// working with C++11 variadic templates). `absl::integer_sequence` is designed
|
||||
// to be a drop-in replacement for C++14's `std::integer_sequence`.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// template< class T, T... Ints >
|
||||
// void user_function(integer_sequence<T, Ints...>);
|
||||
//
|
||||
// int main()
|
||||
// {
|
||||
// // user_function's `T` will be deduced to `int` and `Ints...`
|
||||
// // will be deduced to `0, 1, 2, 3, 4`.
|
||||
// user_function(make_integer_sequence<int, 5>());
|
||||
// }
|
||||
template <typename T, T... Ints>
|
||||
struct integer_sequence
|
||||
{
|
||||
using value_type = T;
|
||||
static constexpr std::size_t size() noexcept
|
||||
{
|
||||
return sizeof...(Ints);
|
||||
}
|
||||
};
|
||||
|
||||
// index_sequence
|
||||
//
|
||||
// A helper template for an `integer_sequence` of `size_t`,
|
||||
// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
|
||||
// `std::index_sequence`.
|
||||
template <size_t... Ints>
|
||||
using index_sequence = integer_sequence<size_t, Ints...>;
|
||||
|
||||
namespace utility_internal
|
||||
{
|
||||
|
||||
template <typename Seq, size_t SeqSize, size_t Rem>
|
||||
struct Extend;
|
||||
|
||||
// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
|
||||
template <typename T, T... Ints, size_t SeqSize>
|
||||
struct Extend<integer_sequence<T, Ints...>, SeqSize, 0>
|
||||
{
|
||||
using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;
|
||||
};
|
||||
|
||||
template <typename T, T... Ints, size_t SeqSize>
|
||||
struct Extend<integer_sequence<T, Ints...>, SeqSize, 1>
|
||||
{
|
||||
using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;
|
||||
};
|
||||
|
||||
// Recursion helper for 'make_integer_sequence<T, N>'.
|
||||
// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
|
||||
template <typename T, size_t N>
|
||||
struct Gen
|
||||
{
|
||||
using type =
|
||||
typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Gen<T, 0>
|
||||
{
|
||||
using type = integer_sequence<T>;
|
||||
};
|
||||
|
||||
} // namespace utility_internal
|
||||
|
||||
// Compile-time sequences of integers
|
||||
|
||||
// make_integer_sequence
|
||||
//
|
||||
// This template alias is equivalent to
|
||||
// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
|
||||
// replacement for C++14's `std::make_integer_sequence`.
|
||||
template <typename T, T N>
|
||||
using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
|
||||
|
||||
// make_index_sequence
|
||||
//
|
||||
// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
|
||||
// and is designed to be a drop-in replacement for C++14's
|
||||
// `std::make_index_sequence`.
|
||||
template <size_t N>
|
||||
using make_index_sequence = make_integer_sequence<size_t, N>;
|
||||
|
||||
// index_sequence_for
|
||||
//
|
||||
// Converts a typename pack into an index sequence of the same length, and
|
||||
// is designed to be a drop-in replacement for C++14's
|
||||
// `std::index_sequence_for()`
|
||||
template <typename... Ts>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
|
||||
|
||||
//// END OF CODE FROM GOOGLE ABSEIL
|
||||
|
||||
#endif
|
||||
|
||||
// dispatch utility (taken from ranges-v3)
|
||||
template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
|
||||
template<> struct priority_tag<0> {};
|
||||
|
||||
// taken from ranges-v3
|
||||
template<typename T>
|
||||
struct static_const
|
||||
{
|
||||
static JSON_INLINE_VARIABLE constexpr T value{};
|
||||
};
|
||||
|
||||
#ifndef JSON_HAS_CPP_17
|
||||
template<typename T>
|
||||
constexpr T static_const<T>::value;
|
||||
#endif
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)
|
||||
{
|
||||
return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,70 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <nlohmann/detail/meta/void_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// https://en.cppreference.com/w/cpp/experimental/is_detected
|
||||
struct nonesuch
|
||||
{
|
||||
nonesuch() = delete;
|
||||
~nonesuch() = delete;
|
||||
nonesuch(nonesuch const&) = delete;
|
||||
nonesuch(nonesuch const&&) = delete;
|
||||
void operator=(nonesuch const&) = delete;
|
||||
void operator=(nonesuch&&) = delete;
|
||||
};
|
||||
|
||||
template<class Default,
|
||||
class AlwaysVoid,
|
||||
template<class...> class Op,
|
||||
class... Args>
|
||||
struct detector
|
||||
{
|
||||
using value_t = std::false_type;
|
||||
using type = Default;
|
||||
};
|
||||
|
||||
template<class Default, template<class...> class Op, class... Args>
|
||||
struct detector<Default, void_t<Op<Args...>>, Op, Args...>
|
||||
{
|
||||
using value_t = std::true_type;
|
||||
using type = Op<Args...>;
|
||||
};
|
||||
|
||||
template<template<class...> class Op, class... Args>
|
||||
using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
|
||||
|
||||
template<template<class...> class Op, class... Args>
|
||||
struct is_detected_lazy : is_detected<Op, Args...> { };
|
||||
|
||||
template<template<class...> class Op, class... Args>
|
||||
using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
|
||||
|
||||
template<class Default, template<class...> class Op, class... Args>
|
||||
using detected_or = detector<Default, void, Op, Args...>;
|
||||
|
||||
template<class Default, template<class...> class Op, class... Args>
|
||||
using detected_or_t = typename detected_or<Default, Op, Args...>::type;
|
||||
|
||||
template<class Expected, template<class...> class Op, class... Args>
|
||||
using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
|
||||
|
||||
template<class To, template<class...> class Op, class... Args>
|
||||
using is_detected_convertible =
|
||||
std::is_convertible<detected_t<Op, Args...>, To>;
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,21 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// dispatching helper struct
|
||||
template <class T> struct identity_tag {};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,159 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // size_t
|
||||
#include <utility> // declval
|
||||
#include <string> // string
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/meta/detected.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
using null_function_t = decltype(std::declval<T&>().null());
|
||||
|
||||
template<typename T>
|
||||
using boolean_function_t =
|
||||
decltype(std::declval<T&>().boolean(std::declval<bool>()));
|
||||
|
||||
template<typename T, typename Integer>
|
||||
using number_integer_function_t =
|
||||
decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
|
||||
|
||||
template<typename T, typename Unsigned>
|
||||
using number_unsigned_function_t =
|
||||
decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));
|
||||
|
||||
template<typename T, typename Float, typename String>
|
||||
using number_float_function_t = decltype(std::declval<T&>().number_float(
|
||||
std::declval<Float>(), std::declval<const String&>()));
|
||||
|
||||
template<typename T, typename String>
|
||||
using string_function_t =
|
||||
decltype(std::declval<T&>().string(std::declval<String&>()));
|
||||
|
||||
template<typename T, typename Binary>
|
||||
using binary_function_t =
|
||||
decltype(std::declval<T&>().binary(std::declval<Binary&>()));
|
||||
|
||||
template<typename T>
|
||||
using start_object_function_t =
|
||||
decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));
|
||||
|
||||
template<typename T, typename String>
|
||||
using key_function_t =
|
||||
decltype(std::declval<T&>().key(std::declval<String&>()));
|
||||
|
||||
template<typename T>
|
||||
using end_object_function_t = decltype(std::declval<T&>().end_object());
|
||||
|
||||
template<typename T>
|
||||
using start_array_function_t =
|
||||
decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
|
||||
|
||||
template<typename T>
|
||||
using end_array_function_t = decltype(std::declval<T&>().end_array());
|
||||
|
||||
template<typename T, typename Exception>
|
||||
using parse_error_function_t = decltype(std::declval<T&>().parse_error(
|
||||
std::declval<std::size_t>(), std::declval<const std::string&>(),
|
||||
std::declval<const Exception&>()));
|
||||
|
||||
template<typename SAX, typename BasicJsonType>
|
||||
struct is_sax
|
||||
{
|
||||
private:
|
||||
static_assert(is_basic_json<BasicJsonType>::value,
|
||||
"BasicJsonType must be of type basic_json<...>");
|
||||
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
using exception_t = typename BasicJsonType::exception;
|
||||
|
||||
public:
|
||||
static constexpr bool value =
|
||||
is_detected_exact<bool, null_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, boolean_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&
|
||||
is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&
|
||||
is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&
|
||||
is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
|
||||
is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&
|
||||
is_detected_exact<bool, start_object_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
|
||||
is_detected_exact<bool, end_object_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, start_array_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, end_array_function_t, SAX>::value &&
|
||||
is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
|
||||
};
|
||||
|
||||
template<typename SAX, typename BasicJsonType>
|
||||
struct is_sax_static_asserts
|
||||
{
|
||||
private:
|
||||
static_assert(is_basic_json<BasicJsonType>::value,
|
||||
"BasicJsonType must be of type basic_json<...>");
|
||||
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using binary_t = typename BasicJsonType::binary_t;
|
||||
using exception_t = typename BasicJsonType::exception;
|
||||
|
||||
public:
|
||||
static_assert(is_detected_exact<bool, null_function_t, SAX>::value,
|
||||
"Missing/invalid function: bool null()");
|
||||
static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
|
||||
"Missing/invalid function: bool boolean(bool)");
|
||||
static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
|
||||
"Missing/invalid function: bool boolean(bool)");
|
||||
static_assert(
|
||||
is_detected_exact<bool, number_integer_function_t, SAX,
|
||||
number_integer_t>::value,
|
||||
"Missing/invalid function: bool number_integer(number_integer_t)");
|
||||
static_assert(
|
||||
is_detected_exact<bool, number_unsigned_function_t, SAX,
|
||||
number_unsigned_t>::value,
|
||||
"Missing/invalid function: bool number_unsigned(number_unsigned_t)");
|
||||
static_assert(is_detected_exact<bool, number_float_function_t, SAX,
|
||||
number_float_t, string_t>::value,
|
||||
"Missing/invalid function: bool number_float(number_float_t, const string_t&)");
|
||||
static_assert(
|
||||
is_detected_exact<bool, string_function_t, SAX, string_t>::value,
|
||||
"Missing/invalid function: bool string(string_t&)");
|
||||
static_assert(
|
||||
is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,
|
||||
"Missing/invalid function: bool binary(binary_t&)");
|
||||
static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,
|
||||
"Missing/invalid function: bool start_object(std::size_t)");
|
||||
static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,
|
||||
"Missing/invalid function: bool key(string_t&)");
|
||||
static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,
|
||||
"Missing/invalid function: bool end_object()");
|
||||
static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,
|
||||
"Missing/invalid function: bool start_array(std::size_t)");
|
||||
static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,
|
||||
"Missing/invalid function: bool end_array()");
|
||||
static_assert(
|
||||
is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,
|
||||
"Missing/invalid function: bool parse_error(std::size_t, const "
|
||||
"std::string&, const exception&)");
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,29 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#include <experimental/filesystem>
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
namespace std_fs = std::experimental::filesystem;
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
#elif JSON_HAS_FILESYSTEM
|
||||
#include <filesystem>
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
namespace std_fs = std::filesystem;
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
#endif
|
||||
@@ -1,795 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits> // numeric_limits
|
||||
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
|
||||
#include <utility> // declval
|
||||
#include <tuple> // tuple
|
||||
#include <string> // char_traits
|
||||
|
||||
#include <nlohmann/detail/iterators/iterator_traits.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/call_std/begin.hpp>
|
||||
#include <nlohmann/detail/meta/call_std/end.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/meta/detected.hpp>
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
/*!
|
||||
@brief detail namespace with internal helper functions
|
||||
|
||||
This namespace collects functions that should not be exposed,
|
||||
implementations of some @ref basic_json methods, and meta-programming helpers.
|
||||
|
||||
@since version 2.1.0
|
||||
*/
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/////////////
|
||||
// helpers //
|
||||
/////////////
|
||||
|
||||
// Note to maintainers:
|
||||
//
|
||||
// Every trait in this file expects a non CV-qualified type.
|
||||
// The only exceptions are in the 'aliases for detected' section
|
||||
// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
|
||||
//
|
||||
// In this case, T has to be properly CV-qualified to constraint the function arguments
|
||||
// (e.g. to_json(BasicJsonType&, const T&))
|
||||
|
||||
template<typename> struct is_basic_json : std::false_type {};
|
||||
|
||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||
struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
|
||||
|
||||
// used by exceptions create() member functions
|
||||
// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t
|
||||
// false_type otherwise
|
||||
template<typename BasicJsonContext>
|
||||
struct is_basic_json_context :
|
||||
std::integral_constant < bool,
|
||||
is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value
|
||||
|| std::is_same<BasicJsonContext, std::nullptr_t>::value >
|
||||
{};
|
||||
|
||||
//////////////////////
|
||||
// json_ref helpers //
|
||||
//////////////////////
|
||||
|
||||
template<typename>
|
||||
class json_ref;
|
||||
|
||||
template<typename>
|
||||
struct is_json_ref : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_json_ref<json_ref<T>> : std::true_type {};
|
||||
|
||||
//////////////////////////
|
||||
// aliases for detected //
|
||||
//////////////////////////
|
||||
|
||||
template<typename T>
|
||||
using mapped_type_t = typename T::mapped_type;
|
||||
|
||||
template<typename T>
|
||||
using key_type_t = typename T::key_type;
|
||||
|
||||
template<typename T>
|
||||
using value_type_t = typename T::value_type;
|
||||
|
||||
template<typename T>
|
||||
using difference_type_t = typename T::difference_type;
|
||||
|
||||
template<typename T>
|
||||
using pointer_t = typename T::pointer;
|
||||
|
||||
template<typename T>
|
||||
using reference_t = typename T::reference;
|
||||
|
||||
template<typename T>
|
||||
using iterator_category_t = typename T::iterator_category;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
using to_json_function = decltype(T::to_json(std::declval<Args>()...));
|
||||
|
||||
template<typename T, typename... Args>
|
||||
using from_json_function = decltype(T::from_json(std::declval<Args>()...));
|
||||
|
||||
template<typename T, typename U>
|
||||
using get_template_function = decltype(std::declval<T>().template get<U>());
|
||||
|
||||
// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
|
||||
template<typename BasicJsonType, typename T, typename = void>
|
||||
struct has_from_json : std::false_type {};
|
||||
|
||||
// trait checking if j.get<T> is valid
|
||||
// use this trait instead of std::is_constructible or std::is_convertible,
|
||||
// both rely on, or make use of implicit conversions, and thus fail when T
|
||||
// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
|
||||
template <typename BasicJsonType, typename T>
|
||||
struct is_getable
|
||||
{
|
||||
static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename T>
|
||||
struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
|
||||
{
|
||||
using serializer = typename BasicJsonType::template json_serializer<T, void>;
|
||||
|
||||
static constexpr bool value =
|
||||
is_detected_exact<void, from_json_function, serializer,
|
||||
const BasicJsonType&, T&>::value;
|
||||
};
|
||||
|
||||
// This trait checks if JSONSerializer<T>::from_json(json const&) exists
|
||||
// this overload is used for non-default-constructible user-defined-types
|
||||
template<typename BasicJsonType, typename T, typename = void>
|
||||
struct has_non_default_from_json : std::false_type {};
|
||||
|
||||
template<typename BasicJsonType, typename T>
|
||||
struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
|
||||
{
|
||||
using serializer = typename BasicJsonType::template json_serializer<T, void>;
|
||||
|
||||
static constexpr bool value =
|
||||
is_detected_exact<T, from_json_function, serializer,
|
||||
const BasicJsonType&>::value;
|
||||
};
|
||||
|
||||
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
|
||||
// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
|
||||
template<typename BasicJsonType, typename T, typename = void>
|
||||
struct has_to_json : std::false_type {};
|
||||
|
||||
template<typename BasicJsonType, typename T>
|
||||
struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
|
||||
{
|
||||
using serializer = typename BasicJsonType::template json_serializer<T, void>;
|
||||
|
||||
static constexpr bool value =
|
||||
is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
|
||||
T>::value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using detect_key_compare = typename T::key_compare;
|
||||
|
||||
template<typename T>
|
||||
struct has_key_compare : std::integral_constant<bool, is_detected<detect_key_compare, T>::value> {};
|
||||
|
||||
// obtains the actual object key comparator
|
||||
template<typename BasicJsonType>
|
||||
struct actual_object_comparator
|
||||
{
|
||||
using object_t = typename BasicJsonType::object_t;
|
||||
using object_comparator_t = typename BasicJsonType::default_object_comparator_t;
|
||||
using type = typename std::conditional < has_key_compare<object_t>::value,
|
||||
typename object_t::key_compare, object_comparator_t>::type;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
|
||||
|
||||
/////////////////
|
||||
// char_traits //
|
||||
/////////////////
|
||||
|
||||
// Primary template of char_traits calls std char_traits
|
||||
template<typename T>
|
||||
struct char_traits : std::char_traits<T>
|
||||
{};
|
||||
|
||||
// Explicitly define char traits for unsigned char since it is not standard
|
||||
template<>
|
||||
struct char_traits<unsigned char> : std::char_traits<char>
|
||||
{
|
||||
using char_type = unsigned char;
|
||||
using int_type = uint64_t;
|
||||
|
||||
// Redefine to_int_type function
|
||||
static int_type to_int_type(char_type c) noexcept
|
||||
{
|
||||
return static_cast<int_type>(c);
|
||||
}
|
||||
|
||||
static char_type to_char_type(int_type i) noexcept
|
||||
{
|
||||
return static_cast<char_type>(i);
|
||||
}
|
||||
|
||||
static constexpr int_type eof() noexcept
|
||||
{
|
||||
return static_cast<int_type>(EOF);
|
||||
}
|
||||
};
|
||||
|
||||
// Explicitly define char traits for signed char since it is not standard
|
||||
template<>
|
||||
struct char_traits<signed char> : std::char_traits<char>
|
||||
{
|
||||
using char_type = signed char;
|
||||
using int_type = uint64_t;
|
||||
|
||||
// Redefine to_int_type function
|
||||
static int_type to_int_type(char_type c) noexcept
|
||||
{
|
||||
return static_cast<int_type>(c);
|
||||
}
|
||||
|
||||
static char_type to_char_type(int_type i) noexcept
|
||||
{
|
||||
return static_cast<char_type>(i);
|
||||
}
|
||||
|
||||
static constexpr int_type eof() noexcept
|
||||
{
|
||||
return static_cast<int_type>(EOF);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////
|
||||
// is_ functions //
|
||||
///////////////////
|
||||
|
||||
// https://en.cppreference.com/w/cpp/types/conjunction
|
||||
template<class...> struct conjunction : std::true_type { };
|
||||
template<class B> struct conjunction<B> : B { };
|
||||
template<class B, class... Bn>
|
||||
struct conjunction<B, Bn...>
|
||||
: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};
|
||||
|
||||
// https://en.cppreference.com/w/cpp/types/negation
|
||||
template<class B> struct negation : std::integral_constant < bool, !B::value > { };
|
||||
|
||||
// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
|
||||
// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
|
||||
// This causes compile errors in e.g. clang 3.5 or gcc 4.9.
|
||||
template <typename T>
|
||||
struct is_default_constructible : std::is_default_constructible<T> {};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct is_default_constructible<std::pair<T1, T2>>
|
||||
: conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct is_default_constructible<const std::pair<T1, T2>>
|
||||
: conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct is_default_constructible<std::tuple<Ts...>>
|
||||
: conjunction<is_default_constructible<Ts>...> {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct is_default_constructible<const std::tuple<Ts...>>
|
||||
: conjunction<is_default_constructible<Ts>...> {};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct is_constructible : std::is_constructible<T, Args...> {};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};
|
||||
|
||||
template <typename T1, typename T2>
|
||||
struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct is_iterator_traits : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_iterator_traits<iterator_traits<T>>
|
||||
{
|
||||
private:
|
||||
using traits = iterator_traits<T>;
|
||||
|
||||
public:
|
||||
static constexpr auto value =
|
||||
is_detected<value_type_t, traits>::value &&
|
||||
is_detected<difference_type_t, traits>::value &&
|
||||
is_detected<pointer_t, traits>::value &&
|
||||
is_detected<iterator_category_t, traits>::value &&
|
||||
is_detected<reference_t, traits>::value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_range
|
||||
{
|
||||
private:
|
||||
using t_ref = typename std::add_lvalue_reference<T>::type;
|
||||
|
||||
using iterator = detected_t<result_of_begin, t_ref>;
|
||||
using sentinel = detected_t<result_of_end, t_ref>;
|
||||
|
||||
// to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator
|
||||
// and https://en.cppreference.com/w/cpp/iterator/sentinel_for
|
||||
// but reimplementing these would be too much work, as a lot of other concepts are used underneath
|
||||
static constexpr auto is_iterator_begin =
|
||||
is_iterator_traits<iterator_traits<iterator>>::value;
|
||||
|
||||
public:
|
||||
static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
using iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;
|
||||
|
||||
template<typename T>
|
||||
using range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;
|
||||
|
||||
// The following implementation of is_complete_type is taken from
|
||||
// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
|
||||
// and is written by Xiang Fan who agreed to using it in this library.
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct is_complete_type : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleObjectType,
|
||||
typename = void>
|
||||
struct is_compatible_object_type_impl : std::false_type {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleObjectType>
|
||||
struct is_compatible_object_type_impl <
|
||||
BasicJsonType, CompatibleObjectType,
|
||||
enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&
|
||||
is_detected<key_type_t, CompatibleObjectType>::value >>
|
||||
{
|
||||
using object_t = typename BasicJsonType::object_t;
|
||||
|
||||
// macOS's is_constructible does not play well with nonesuch...
|
||||
static constexpr bool value =
|
||||
is_constructible<typename object_t::key_type,
|
||||
typename CompatibleObjectType::key_type>::value &&
|
||||
is_constructible<typename object_t::mapped_type,
|
||||
typename CompatibleObjectType::mapped_type>::value;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleObjectType>
|
||||
struct is_compatible_object_type
|
||||
: is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||
typename = void>
|
||||
struct is_constructible_object_type_impl : std::false_type {};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleObjectType>
|
||||
struct is_constructible_object_type_impl <
|
||||
BasicJsonType, ConstructibleObjectType,
|
||||
enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&
|
||||
is_detected<key_type_t, ConstructibleObjectType>::value >>
|
||||
{
|
||||
using object_t = typename BasicJsonType::object_t;
|
||||
|
||||
static constexpr bool value =
|
||||
(is_default_constructible<ConstructibleObjectType>::value &&
|
||||
(std::is_move_assignable<ConstructibleObjectType>::value ||
|
||||
std::is_copy_assignable<ConstructibleObjectType>::value) &&
|
||||
(is_constructible<typename ConstructibleObjectType::key_type,
|
||||
typename object_t::key_type>::value &&
|
||||
std::is_same <
|
||||
typename object_t::mapped_type,
|
||||
typename ConstructibleObjectType::mapped_type >::value)) ||
|
||||
(has_from_json<BasicJsonType,
|
||||
typename ConstructibleObjectType::mapped_type>::value ||
|
||||
has_non_default_from_json <
|
||||
BasicJsonType,
|
||||
typename ConstructibleObjectType::mapped_type >::value);
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleObjectType>
|
||||
struct is_constructible_object_type
|
||||
: is_constructible_object_type_impl<BasicJsonType,
|
||||
ConstructibleObjectType> {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleStringType>
|
||||
struct is_compatible_string_type
|
||||
{
|
||||
static constexpr auto value =
|
||||
is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleStringType>
|
||||
struct is_constructible_string_type
|
||||
{
|
||||
// launder type through decltype() to fix compilation failure on ICPC
|
||||
#ifdef __INTEL_COMPILER
|
||||
using laundered_type = decltype(std::declval<ConstructibleStringType>());
|
||||
#else
|
||||
using laundered_type = ConstructibleStringType;
|
||||
#endif
|
||||
|
||||
static constexpr auto value =
|
||||
conjunction <
|
||||
is_constructible<laundered_type, typename BasicJsonType::string_t>,
|
||||
is_detected_exact<typename BasicJsonType::string_t::value_type,
|
||||
value_type_t, laundered_type >>::value;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
|
||||
struct is_compatible_array_type_impl : std::false_type {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||
struct is_compatible_array_type_impl <
|
||||
BasicJsonType, CompatibleArrayType,
|
||||
enable_if_t <
|
||||
is_detected<iterator_t, CompatibleArrayType>::value&&
|
||||
is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
|
||||
// special case for types like std::filesystem::path whose iterator's value_type are themselves
|
||||
// c.f. https://github.com/nlohmann/json/pull/3073
|
||||
!std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
|
||||
{
|
||||
static constexpr bool value =
|
||||
is_constructible<BasicJsonType,
|
||||
range_value_t<CompatibleArrayType>>::value;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||
struct is_compatible_array_type
|
||||
: is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
|
||||
struct is_constructible_array_type_impl : std::false_type {};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleArrayType>
|
||||
struct is_constructible_array_type_impl <
|
||||
BasicJsonType, ConstructibleArrayType,
|
||||
enable_if_t<std::is_same<ConstructibleArrayType,
|
||||
typename BasicJsonType::value_type>::value >>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleArrayType>
|
||||
struct is_constructible_array_type_impl <
|
||||
BasicJsonType, ConstructibleArrayType,
|
||||
enable_if_t < !std::is_same<ConstructibleArrayType,
|
||||
typename BasicJsonType::value_type>::value&&
|
||||
!is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
is_default_constructible<ConstructibleArrayType>::value&&
|
||||
(std::is_move_assignable<ConstructibleArrayType>::value ||
|
||||
std::is_copy_assignable<ConstructibleArrayType>::value)&&
|
||||
is_detected<iterator_t, ConstructibleArrayType>::value&&
|
||||
is_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&
|
||||
is_detected<range_value_t, ConstructibleArrayType>::value&&
|
||||
// special case for types like std::filesystem::path whose iterator's value_type are themselves
|
||||
// c.f. https://github.com/nlohmann/json/pull/3073
|
||||
!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&
|
||||
is_complete_type <
|
||||
detected_t<range_value_t, ConstructibleArrayType >>::value >>
|
||||
{
|
||||
using value_type = range_value_t<ConstructibleArrayType>;
|
||||
|
||||
static constexpr bool value =
|
||||
std::is_same<value_type,
|
||||
typename BasicJsonType::array_t::value_type>::value ||
|
||||
has_from_json<BasicJsonType,
|
||||
value_type>::value ||
|
||||
has_non_default_from_json <
|
||||
BasicJsonType,
|
||||
value_type >::value;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleArrayType>
|
||||
struct is_constructible_array_type
|
||||
: is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
|
||||
|
||||
template<typename RealIntegerType, typename CompatibleNumberIntegerType,
|
||||
typename = void>
|
||||
struct is_compatible_integer_type_impl : std::false_type {};
|
||||
|
||||
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
|
||||
struct is_compatible_integer_type_impl <
|
||||
RealIntegerType, CompatibleNumberIntegerType,
|
||||
enable_if_t < std::is_integral<RealIntegerType>::value&&
|
||||
std::is_integral<CompatibleNumberIntegerType>::value&&
|
||||
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
|
||||
{
|
||||
// is there an assert somewhere on overflows?
|
||||
using RealLimits = std::numeric_limits<RealIntegerType>;
|
||||
using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
|
||||
|
||||
static constexpr auto value =
|
||||
is_constructible<RealIntegerType,
|
||||
CompatibleNumberIntegerType>::value &&
|
||||
CompatibleLimits::is_integer &&
|
||||
RealLimits::is_signed == CompatibleLimits::is_signed;
|
||||
};
|
||||
|
||||
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
|
||||
struct is_compatible_integer_type
|
||||
: is_compatible_integer_type_impl<RealIntegerType,
|
||||
CompatibleNumberIntegerType> {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleType, typename = void>
|
||||
struct is_compatible_type_impl: std::false_type {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleType>
|
||||
struct is_compatible_type_impl <
|
||||
BasicJsonType, CompatibleType,
|
||||
enable_if_t<is_complete_type<CompatibleType>::value >>
|
||||
{
|
||||
static constexpr bool value =
|
||||
has_to_json<BasicJsonType, CompatibleType>::value;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleType>
|
||||
struct is_compatible_type
|
||||
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
||||
|
||||
template<typename T1, typename T2>
|
||||
struct is_constructible_tuple : std::false_type {};
|
||||
|
||||
template<typename T1, typename... Args>
|
||||
struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};
|
||||
|
||||
template<typename BasicJsonType, typename T>
|
||||
struct is_json_iterator_of : std::false_type {};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::iterator> : std::true_type {};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::const_iterator> : std::true_type
|
||||
{};
|
||||
|
||||
// checks if a given type T is a template specialization of Primary
|
||||
template<template <typename...> class Primary, typename T>
|
||||
struct is_specialization_of : std::false_type {};
|
||||
|
||||
template<template <typename...> class Primary, typename... Args>
|
||||
struct is_specialization_of<Primary, Primary<Args...>> : std::true_type {};
|
||||
|
||||
template<typename T>
|
||||
using is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t<T>>;
|
||||
|
||||
// checks if A and B are comparable using Compare functor
|
||||
template<typename Compare, typename A, typename B, typename = void>
|
||||
struct is_comparable : std::false_type {};
|
||||
|
||||
template<typename Compare, typename A, typename B>
|
||||
struct is_comparable<Compare, A, B, void_t<
|
||||
decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())),
|
||||
decltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))
|
||||
>> : std::true_type {};
|
||||
|
||||
template<typename T>
|
||||
using detect_is_transparent = typename T::is_transparent;
|
||||
|
||||
// type trait to check if KeyType can be used as object key (without a BasicJsonType)
|
||||
// see is_usable_as_basic_json_key_type below
|
||||
template<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
|
||||
bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
|
||||
using is_usable_as_key_type = typename std::conditional <
|
||||
is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value
|
||||
&& !(ExcludeObjectKeyType && std::is_same<KeyType,
|
||||
ObjectKeyType>::value)
|
||||
&& (!RequireTransparentComparator
|
||||
|| is_detected <detect_is_transparent, Comparator>::value)
|
||||
&& !is_json_pointer<KeyType>::value,
|
||||
std::true_type,
|
||||
std::false_type >::type;
|
||||
|
||||
// type trait to check if KeyType can be used as object key
|
||||
// true if:
|
||||
// - KeyType is comparable with BasicJsonType::object_t::key_type
|
||||
// - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type
|
||||
// - the comparator is transparent or RequireTransparentComparator is false
|
||||
// - KeyType is not a JSON iterator or json_pointer
|
||||
template<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
|
||||
bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
|
||||
using is_usable_as_basic_json_key_type = typename std::conditional <
|
||||
is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
|
||||
typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
|
||||
RequireTransparentComparator, ExcludeObjectKeyType>::value
|
||||
&& !is_json_iterator_of<BasicJsonType, KeyType>::value,
|
||||
std::true_type,
|
||||
std::false_type >::type;
|
||||
|
||||
template<typename ObjectType, typename KeyType>
|
||||
using detect_erase_with_key_type = decltype(std::declval<ObjectType&>().erase(std::declval<KeyType>()));
|
||||
|
||||
// type trait to check if object_t has an erase() member functions accepting KeyType
|
||||
template<typename BasicJsonType, typename KeyType>
|
||||
using has_erase_with_key_type = typename std::conditional <
|
||||
is_detected <
|
||||
detect_erase_with_key_type,
|
||||
typename BasicJsonType::object_t, KeyType >::value,
|
||||
std::true_type,
|
||||
std::false_type >::type;
|
||||
|
||||
// a naive helper to check if a type is an ordered_map (exploits the fact that
|
||||
// ordered_map inherits capacity() from std::vector)
|
||||
template <typename T>
|
||||
struct is_ordered_map
|
||||
{
|
||||
using one = char;
|
||||
|
||||
struct two
|
||||
{
|
||||
char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
};
|
||||
|
||||
template <typename C> static one test( decltype(&C::capacity) ) ;
|
||||
template <typename C> static two test(...);
|
||||
|
||||
enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
};
|
||||
|
||||
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
|
||||
template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
|
||||
T conditional_static_cast(U value)
|
||||
{
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
|
||||
template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
|
||||
T conditional_static_cast(U value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename... Types>
|
||||
using all_integral = conjunction<std::is_integral<Types>...>;
|
||||
|
||||
template<typename... Types>
|
||||
using all_signed = conjunction<std::is_signed<Types>...>;
|
||||
|
||||
template<typename... Types>
|
||||
using all_unsigned = conjunction<std::is_unsigned<Types>...>;
|
||||
|
||||
// there's a disjunction trait in another PR; replace when merged
|
||||
template<typename... Types>
|
||||
using same_sign = std::integral_constant < bool,
|
||||
all_signed<Types...>::value || all_unsigned<Types...>::value >;
|
||||
|
||||
template<typename OfType, typename T>
|
||||
using never_out_of_range = std::integral_constant < bool,
|
||||
(std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType)))
|
||||
|| (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T)) >;
|
||||
|
||||
template<typename OfType, typename T,
|
||||
bool OfTypeSigned = std::is_signed<OfType>::value,
|
||||
bool TSigned = std::is_signed<T>::value>
|
||||
struct value_in_range_of_impl2;
|
||||
|
||||
template<typename OfType, typename T>
|
||||
struct value_in_range_of_impl2<OfType, T, false, false>
|
||||
{
|
||||
static constexpr bool test(T val)
|
||||
{
|
||||
using CommonType = typename std::common_type<OfType, T>::type;
|
||||
return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename OfType, typename T>
|
||||
struct value_in_range_of_impl2<OfType, T, true, false>
|
||||
{
|
||||
static constexpr bool test(T val)
|
||||
{
|
||||
using CommonType = typename std::common_type<OfType, T>::type;
|
||||
return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename OfType, typename T>
|
||||
struct value_in_range_of_impl2<OfType, T, false, true>
|
||||
{
|
||||
static constexpr bool test(T val)
|
||||
{
|
||||
using CommonType = typename std::common_type<OfType, T>::type;
|
||||
return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename OfType, typename T>
|
||||
struct value_in_range_of_impl2<OfType, T, true, true>
|
||||
{
|
||||
static constexpr bool test(T val)
|
||||
{
|
||||
using CommonType = typename std::common_type<OfType, T>::type;
|
||||
return static_cast<CommonType>(val) >= static_cast<CommonType>((std::numeric_limits<OfType>::min)())
|
||||
&& static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename OfType, typename T,
|
||||
bool NeverOutOfRange = never_out_of_range<OfType, T>::value,
|
||||
typename = detail::enable_if_t<all_integral<OfType, T>::value>>
|
||||
struct value_in_range_of_impl1;
|
||||
|
||||
template<typename OfType, typename T>
|
||||
struct value_in_range_of_impl1<OfType, T, false>
|
||||
{
|
||||
static constexpr bool test(T val)
|
||||
{
|
||||
return value_in_range_of_impl2<OfType, T>::test(val);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename OfType, typename T>
|
||||
struct value_in_range_of_impl1<OfType, T, true>
|
||||
{
|
||||
static constexpr bool test(T /*val*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename OfType, typename T>
|
||||
inline constexpr bool value_in_range_of(T val)
|
||||
{
|
||||
return value_in_range_of_impl1<OfType, T>::test(val);
|
||||
}
|
||||
|
||||
template<bool Value>
|
||||
using bool_constant = std::integral_constant<bool, Value>;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// is_c_string
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace impl
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_c_string()
|
||||
{
|
||||
using TUnExt = typename std::remove_extent<T>::type;
|
||||
using TUnCVExt = typename std::remove_cv<TUnExt>::type;
|
||||
using TUnPtr = typename std::remove_pointer<T>::type;
|
||||
using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;
|
||||
return
|
||||
(std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)
|
||||
|| (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
// checks whether T is a [cv] char */[cv] char[] C string
|
||||
template<typename T>
|
||||
struct is_c_string : bool_constant<impl::is_c_string<T>()> {};
|
||||
|
||||
template<typename T>
|
||||
using is_c_string_uncvref = is_c_string<uncvref_t<T>>;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// is_transparent
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace impl
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_transparent()
|
||||
{
|
||||
return is_detected<detect_is_transparent, T>::value;
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
// checks whether T has a member named is_transparent
|
||||
template<typename T>
|
||||
struct is_transparent : bool_constant<impl::is_transparent<T>()> {};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,24 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename ...Ts> struct make_void
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
template<typename ...Ts> using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,147 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // copy
|
||||
#include <cstddef> // size_t
|
||||
#include <iterator> // back_inserter
|
||||
#include <memory> // shared_ptr, make_shared
|
||||
#include <string> // basic_string
|
||||
#include <vector> // vector
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
#include <ios> // streamsize
|
||||
#include <ostream> // basic_ostream
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// abstract output adapter interface
|
||||
template<typename CharType> struct output_adapter_protocol
|
||||
{
|
||||
virtual void write_character(CharType c) = 0;
|
||||
virtual void write_characters(const CharType* s, std::size_t length) = 0;
|
||||
virtual ~output_adapter_protocol() = default;
|
||||
|
||||
output_adapter_protocol() = default;
|
||||
output_adapter_protocol(const output_adapter_protocol&) = default;
|
||||
output_adapter_protocol(output_adapter_protocol&&) noexcept = default;
|
||||
output_adapter_protocol& operator=(const output_adapter_protocol&) = default;
|
||||
output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;
|
||||
};
|
||||
|
||||
/// a type to simplify interfaces
|
||||
template<typename CharType>
|
||||
using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
|
||||
|
||||
/// output adapter for byte vectors
|
||||
template<typename CharType, typename AllocatorType = std::allocator<CharType>>
|
||||
class output_vector_adapter : public output_adapter_protocol<CharType>
|
||||
{
|
||||
public:
|
||||
explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept
|
||||
: v(vec)
|
||||
{}
|
||||
|
||||
void write_character(CharType c) override
|
||||
{
|
||||
v.push_back(c);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
void write_characters(const CharType* s, std::size_t length) override
|
||||
{
|
||||
v.insert(v.end(), s, s + length);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<CharType, AllocatorType>& v;
|
||||
};
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
/// output adapter for output streams
|
||||
template<typename CharType>
|
||||
class output_stream_adapter : public output_adapter_protocol<CharType>
|
||||
{
|
||||
public:
|
||||
explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept
|
||||
: stream(s)
|
||||
{}
|
||||
|
||||
void write_character(CharType c) override
|
||||
{
|
||||
stream.put(c);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
void write_characters(const CharType* s, std::size_t length) override
|
||||
{
|
||||
stream.write(s, static_cast<std::streamsize>(length));
|
||||
}
|
||||
|
||||
private:
|
||||
std::basic_ostream<CharType>& stream;
|
||||
};
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
/// output adapter for basic_string
|
||||
template<typename CharType, typename StringType = std::basic_string<CharType>>
|
||||
class output_string_adapter : public output_adapter_protocol<CharType>
|
||||
{
|
||||
public:
|
||||
explicit output_string_adapter(StringType& s) noexcept
|
||||
: str(s)
|
||||
{}
|
||||
|
||||
void write_character(CharType c) override
|
||||
{
|
||||
str.push_back(c);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
void write_characters(const CharType* s, std::size_t length) override
|
||||
{
|
||||
str.append(s, length);
|
||||
}
|
||||
|
||||
private:
|
||||
StringType& str;
|
||||
};
|
||||
|
||||
template<typename CharType, typename StringType = std::basic_string<CharType>>
|
||||
class output_adapter
|
||||
{
|
||||
public:
|
||||
template<typename AllocatorType = std::allocator<CharType>>
|
||||
output_adapter(std::vector<CharType, AllocatorType>& vec)
|
||||
: oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}
|
||||
|
||||
#ifndef JSON_NO_IO
|
||||
output_adapter(std::basic_ostream<CharType>& s)
|
||||
: oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
output_adapter(StringType& s)
|
||||
: oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
|
||||
|
||||
operator output_adapter_t<CharType>()
|
||||
{
|
||||
return oa;
|
||||
}
|
||||
|
||||
private:
|
||||
output_adapter_t<CharType> oa = nullptr;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,988 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2008-2009 Björn Hoehrmann <bjoern@hoehrmann.de>
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // reverse, remove, fill, find, none_of
|
||||
#include <array> // array
|
||||
#include <clocale> // localeconv, lconv
|
||||
#include <cmath> // labs, isfinite, isnan, signbit
|
||||
#include <cstddef> // size_t, ptrdiff_t
|
||||
#include <cstdint> // uint8_t
|
||||
#include <cstdio> // snprintf
|
||||
#include <limits> // numeric_limits
|
||||
#include <string> // string, char_traits
|
||||
#include <iomanip> // setfill, setw
|
||||
#include <type_traits> // is_same
|
||||
#include <utility> // move
|
||||
|
||||
#include <nlohmann/detail/conversions/to_chars.hpp>
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/output/binary_writer.hpp>
|
||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
///////////////////
|
||||
// serialization //
|
||||
///////////////////
|
||||
|
||||
/// how to treat decoding errors
|
||||
enum class error_handler_t
|
||||
{
|
||||
strict, ///< throw a type_error exception in case of invalid UTF-8
|
||||
replace, ///< replace invalid UTF-8 sequences with U+FFFD
|
||||
ignore ///< ignore invalid UTF-8 sequences
|
||||
};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class serializer
|
||||
{
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using binary_char_t = typename BasicJsonType::binary_t::value_type;
|
||||
static constexpr std::uint8_t UTF8_ACCEPT = 0;
|
||||
static constexpr std::uint8_t UTF8_REJECT = 1;
|
||||
|
||||
public:
|
||||
/*!
|
||||
@param[in] s output stream to serialize to
|
||||
@param[in] ichar indentation character to use
|
||||
@param[in] error_handler_ how to react on decoding errors
|
||||
*/
|
||||
serializer(output_adapter_t<char> s, const char ichar,
|
||||
error_handler_t error_handler_ = error_handler_t::strict)
|
||||
: o(std::move(s))
|
||||
, loc(std::localeconv())
|
||||
, thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
|
||||
, decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
|
||||
, indent_char(ichar)
|
||||
, indent_string(512, indent_char)
|
||||
, error_handler(error_handler_)
|
||||
{}
|
||||
|
||||
// delete because of pointer members
|
||||
serializer(const serializer&) = delete;
|
||||
serializer& operator=(const serializer&) = delete;
|
||||
serializer(serializer&&) = delete;
|
||||
serializer& operator=(serializer&&) = delete;
|
||||
~serializer() = default;
|
||||
|
||||
/*!
|
||||
@brief internal implementation of the serialization function
|
||||
|
||||
This function is called by the public member function dump and organizes
|
||||
the serialization internally. The indentation level is propagated as
|
||||
additional parameter. In case of arrays and objects, the function is
|
||||
called recursively.
|
||||
|
||||
- strings and object keys are escaped using `escape_string()`
|
||||
- integer numbers are converted implicitly via `operator<<`
|
||||
- floating-point numbers are converted to a string using `"%g"` format
|
||||
- binary values are serialized as objects containing the subtype and the
|
||||
byte array
|
||||
|
||||
@param[in] val value to serialize
|
||||
@param[in] pretty_print whether the output shall be pretty-printed
|
||||
@param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
|
||||
in the output are escaped with `\uXXXX` sequences, and the result consists
|
||||
of ASCII characters only.
|
||||
@param[in] indent_step the indent level
|
||||
@param[in] current_indent the current indent level (only used internally)
|
||||
*/
|
||||
void dump(const BasicJsonType& val,
|
||||
const bool pretty_print,
|
||||
const bool ensure_ascii,
|
||||
const unsigned int indent_step,
|
||||
const unsigned int current_indent = 0)
|
||||
{
|
||||
switch (val.m_data.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
if (val.m_data.m_value.object->empty())
|
||||
{
|
||||
o->write_characters("{}", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pretty_print)
|
||||
{
|
||||
o->write_characters("{\n", 2);
|
||||
|
||||
// variable to hold indentation for recursive calls
|
||||
const auto new_indent = current_indent + indent_step;
|
||||
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
|
||||
{
|
||||
indent_string.resize(indent_string.size() * 2, ' ');
|
||||
}
|
||||
|
||||
// first n-1 elements
|
||||
auto i = val.m_data.m_value.object->cbegin();
|
||||
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
||||
{
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
o->write_character('\"');
|
||||
dump_escaped(i->first, ensure_ascii);
|
||||
o->write_characters("\": ", 3);
|
||||
dump(i->second, true, ensure_ascii, indent_step, new_indent);
|
||||
o->write_characters(",\n", 2);
|
||||
}
|
||||
|
||||
// last element
|
||||
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
||||
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
o->write_character('\"');
|
||||
dump_escaped(i->first, ensure_ascii);
|
||||
o->write_characters("\": ", 3);
|
||||
dump(i->second, true, ensure_ascii, indent_step, new_indent);
|
||||
|
||||
o->write_character('\n');
|
||||
o->write_characters(indent_string.c_str(), current_indent);
|
||||
o->write_character('}');
|
||||
}
|
||||
else
|
||||
{
|
||||
o->write_character('{');
|
||||
|
||||
// first n-1 elements
|
||||
auto i = val.m_data.m_value.object->cbegin();
|
||||
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
|
||||
{
|
||||
o->write_character('\"');
|
||||
dump_escaped(i->first, ensure_ascii);
|
||||
o->write_characters("\":", 2);
|
||||
dump(i->second, false, ensure_ascii, indent_step, current_indent);
|
||||
o->write_character(',');
|
||||
}
|
||||
|
||||
// last element
|
||||
JSON_ASSERT(i != val.m_data.m_value.object->cend());
|
||||
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
|
||||
o->write_character('\"');
|
||||
dump_escaped(i->first, ensure_ascii);
|
||||
o->write_characters("\":", 2);
|
||||
dump(i->second, false, ensure_ascii, indent_step, current_indent);
|
||||
|
||||
o->write_character('}');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
if (val.m_data.m_value.array->empty())
|
||||
{
|
||||
o->write_characters("[]", 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pretty_print)
|
||||
{
|
||||
o->write_characters("[\n", 2);
|
||||
|
||||
// variable to hold indentation for recursive calls
|
||||
const auto new_indent = current_indent + indent_step;
|
||||
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
|
||||
{
|
||||
indent_string.resize(indent_string.size() * 2, ' ');
|
||||
}
|
||||
|
||||
// first n-1 elements
|
||||
for (auto i = val.m_data.m_value.array->cbegin();
|
||||
i != val.m_data.m_value.array->cend() - 1; ++i)
|
||||
{
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
dump(*i, true, ensure_ascii, indent_step, new_indent);
|
||||
o->write_characters(",\n", 2);
|
||||
}
|
||||
|
||||
// last element
|
||||
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
dump(val.m_data.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
|
||||
|
||||
o->write_character('\n');
|
||||
o->write_characters(indent_string.c_str(), current_indent);
|
||||
o->write_character(']');
|
||||
}
|
||||
else
|
||||
{
|
||||
o->write_character('[');
|
||||
|
||||
// first n-1 elements
|
||||
for (auto i = val.m_data.m_value.array->cbegin();
|
||||
i != val.m_data.m_value.array->cend() - 1; ++i)
|
||||
{
|
||||
dump(*i, false, ensure_ascii, indent_step, current_indent);
|
||||
o->write_character(',');
|
||||
}
|
||||
|
||||
// last element
|
||||
JSON_ASSERT(!val.m_data.m_value.array->empty());
|
||||
dump(val.m_data.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
|
||||
|
||||
o->write_character(']');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
o->write_character('\"');
|
||||
dump_escaped(*val.m_data.m_value.string, ensure_ascii);
|
||||
o->write_character('\"');
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::binary:
|
||||
{
|
||||
if (pretty_print)
|
||||
{
|
||||
o->write_characters("{\n", 2);
|
||||
|
||||
// variable to hold indentation for recursive calls
|
||||
const auto new_indent = current_indent + indent_step;
|
||||
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
|
||||
{
|
||||
indent_string.resize(indent_string.size() * 2, ' ');
|
||||
}
|
||||
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
|
||||
o->write_characters("\"bytes\": [", 10);
|
||||
|
||||
if (!val.m_data.m_value.binary->empty())
|
||||
{
|
||||
for (auto i = val.m_data.m_value.binary->cbegin();
|
||||
i != val.m_data.m_value.binary->cend() - 1; ++i)
|
||||
{
|
||||
dump_integer(*i);
|
||||
o->write_characters(", ", 2);
|
||||
}
|
||||
dump_integer(val.m_data.m_value.binary->back());
|
||||
}
|
||||
|
||||
o->write_characters("],\n", 3);
|
||||
o->write_characters(indent_string.c_str(), new_indent);
|
||||
|
||||
o->write_characters("\"subtype\": ", 11);
|
||||
if (val.m_data.m_value.binary->has_subtype())
|
||||
{
|
||||
dump_integer(val.m_data.m_value.binary->subtype());
|
||||
}
|
||||
else
|
||||
{
|
||||
o->write_characters("null", 4);
|
||||
}
|
||||
o->write_character('\n');
|
||||
o->write_characters(indent_string.c_str(), current_indent);
|
||||
o->write_character('}');
|
||||
}
|
||||
else
|
||||
{
|
||||
o->write_characters("{\"bytes\":[", 10);
|
||||
|
||||
if (!val.m_data.m_value.binary->empty())
|
||||
{
|
||||
for (auto i = val.m_data.m_value.binary->cbegin();
|
||||
i != val.m_data.m_value.binary->cend() - 1; ++i)
|
||||
{
|
||||
dump_integer(*i);
|
||||
o->write_character(',');
|
||||
}
|
||||
dump_integer(val.m_data.m_value.binary->back());
|
||||
}
|
||||
|
||||
o->write_characters("],\"subtype\":", 12);
|
||||
if (val.m_data.m_value.binary->has_subtype())
|
||||
{
|
||||
dump_integer(val.m_data.m_value.binary->subtype());
|
||||
o->write_character('}');
|
||||
}
|
||||
else
|
||||
{
|
||||
o->write_characters("null}", 5);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::boolean:
|
||||
{
|
||||
if (val.m_data.m_value.boolean)
|
||||
{
|
||||
o->write_characters("true", 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
o->write_characters("false", 5);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_integer:
|
||||
{
|
||||
dump_integer(val.m_data.m_value.number_integer);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
dump_integer(val.m_data.m_value.number_unsigned);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_float:
|
||||
{
|
||||
dump_float(val.m_data.m_value.number_float);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::discarded:
|
||||
{
|
||||
o->write_characters("<discarded>", 11);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
{
|
||||
o->write_characters("null", 4);
|
||||
return;
|
||||
}
|
||||
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
/*!
|
||||
@brief dump escaped string
|
||||
|
||||
Escape a string by replacing certain special characters by a sequence of an
|
||||
escape character (backslash) and another character and other control
|
||||
characters by a sequence of "\u" followed by a four-digit hex
|
||||
representation. The escaped string is written to output stream @a o.
|
||||
|
||||
@param[in] s the string to escape
|
||||
@param[in] ensure_ascii whether to escape non-ASCII characters with
|
||||
\uXXXX sequences
|
||||
|
||||
@complexity Linear in the length of string @a s.
|
||||
*/
|
||||
void dump_escaped(const string_t& s, const bool ensure_ascii)
|
||||
{
|
||||
std::uint32_t codepoint{};
|
||||
std::uint8_t state = UTF8_ACCEPT;
|
||||
std::size_t bytes = 0; // number of bytes written to string_buffer
|
||||
|
||||
// number of bytes written at the point of the last valid byte
|
||||
std::size_t bytes_after_last_accept = 0;
|
||||
std::size_t undumped_chars = 0;
|
||||
|
||||
for (std::size_t i = 0; i < s.size(); ++i)
|
||||
{
|
||||
const auto byte = static_cast<std::uint8_t>(s[i]);
|
||||
|
||||
switch (decode(state, codepoint, byte))
|
||||
{
|
||||
case UTF8_ACCEPT: // decode found a new code point
|
||||
{
|
||||
switch (codepoint)
|
||||
{
|
||||
case 0x08: // backspace
|
||||
{
|
||||
string_buffer[bytes++] = '\\';
|
||||
string_buffer[bytes++] = 'b';
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x09: // horizontal tab
|
||||
{
|
||||
string_buffer[bytes++] = '\\';
|
||||
string_buffer[bytes++] = 't';
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0A: // newline
|
||||
{
|
||||
string_buffer[bytes++] = '\\';
|
||||
string_buffer[bytes++] = 'n';
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0C: // formfeed
|
||||
{
|
||||
string_buffer[bytes++] = '\\';
|
||||
string_buffer[bytes++] = 'f';
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0D: // carriage return
|
||||
{
|
||||
string_buffer[bytes++] = '\\';
|
||||
string_buffer[bytes++] = 'r';
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x22: // quotation mark
|
||||
{
|
||||
string_buffer[bytes++] = '\\';
|
||||
string_buffer[bytes++] = '\"';
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x5C: // reverse solidus
|
||||
{
|
||||
string_buffer[bytes++] = '\\';
|
||||
string_buffer[bytes++] = '\\';
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// escape control characters (0x00..0x1F) or, if
|
||||
// ensure_ascii parameter is used, non-ASCII characters
|
||||
if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
|
||||
{
|
||||
if (codepoint <= 0xFFFF)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
|
||||
static_cast<std::uint16_t>(codepoint)));
|
||||
bytes += 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
|
||||
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
|
||||
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
|
||||
bytes += 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy byte to buffer (all previous bytes
|
||||
// been copied have in default case above)
|
||||
string_buffer[bytes++] = s[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// write buffer and reset index; there must be 13 bytes
|
||||
// left, as this is the maximal number of bytes to be
|
||||
// written ("\uxxxx\uxxxx\0") for one code point
|
||||
if (string_buffer.size() - bytes < 13)
|
||||
{
|
||||
o->write_characters(string_buffer.data(), bytes);
|
||||
bytes = 0;
|
||||
}
|
||||
|
||||
// remember the byte position of this accept
|
||||
bytes_after_last_accept = bytes;
|
||||
undumped_chars = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case UTF8_REJECT: // decode found invalid UTF-8 byte
|
||||
{
|
||||
switch (error_handler)
|
||||
{
|
||||
case error_handler_t::strict:
|
||||
{
|
||||
JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
|
||||
}
|
||||
|
||||
case error_handler_t::ignore:
|
||||
case error_handler_t::replace:
|
||||
{
|
||||
// in case we saw this character the first time, we
|
||||
// would like to read it again, because the byte
|
||||
// may be OK for itself, but just not OK for the
|
||||
// previous sequence
|
||||
if (undumped_chars > 0)
|
||||
{
|
||||
--i;
|
||||
}
|
||||
|
||||
// reset length buffer to the last accepted index;
|
||||
// thus removing/ignoring the invalid characters
|
||||
bytes = bytes_after_last_accept;
|
||||
|
||||
if (error_handler == error_handler_t::replace)
|
||||
{
|
||||
// add a replacement character
|
||||
if (ensure_ascii)
|
||||
{
|
||||
string_buffer[bytes++] = '\\';
|
||||
string_buffer[bytes++] = 'u';
|
||||
string_buffer[bytes++] = 'f';
|
||||
string_buffer[bytes++] = 'f';
|
||||
string_buffer[bytes++] = 'f';
|
||||
string_buffer[bytes++] = 'd';
|
||||
}
|
||||
else
|
||||
{
|
||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
|
||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
|
||||
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
|
||||
}
|
||||
|
||||
// write buffer and reset index; there must be 13 bytes
|
||||
// left, as this is the maximal number of bytes to be
|
||||
// written ("\uxxxx\uxxxx\0") for one code point
|
||||
if (string_buffer.size() - bytes < 13)
|
||||
{
|
||||
o->write_characters(string_buffer.data(), bytes);
|
||||
bytes = 0;
|
||||
}
|
||||
|
||||
bytes_after_last_accept = bytes;
|
||||
}
|
||||
|
||||
undumped_chars = 0;
|
||||
|
||||
// continue processing the string
|
||||
state = UTF8_ACCEPT;
|
||||
break;
|
||||
}
|
||||
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: // decode found yet incomplete multi-byte code point
|
||||
{
|
||||
if (!ensure_ascii)
|
||||
{
|
||||
// code point will not be escaped - copy byte to buffer
|
||||
string_buffer[bytes++] = s[i];
|
||||
}
|
||||
++undumped_chars;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we finished processing the string
|
||||
if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
|
||||
{
|
||||
// write buffer
|
||||
if (bytes > 0)
|
||||
{
|
||||
o->write_characters(string_buffer.data(), bytes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we finish reading, but do not accept: string was incomplete
|
||||
switch (error_handler)
|
||||
{
|
||||
case error_handler_t::strict:
|
||||
{
|
||||
JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s.back() | 0))), nullptr));
|
||||
}
|
||||
|
||||
case error_handler_t::ignore:
|
||||
{
|
||||
// write all accepted bytes
|
||||
o->write_characters(string_buffer.data(), bytes_after_last_accept);
|
||||
break;
|
||||
}
|
||||
|
||||
case error_handler_t::replace:
|
||||
{
|
||||
// write all accepted bytes
|
||||
o->write_characters(string_buffer.data(), bytes_after_last_accept);
|
||||
// add a replacement character
|
||||
if (ensure_ascii)
|
||||
{
|
||||
o->write_characters("\\ufffd", 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
o->write_characters("\xEF\xBF\xBD", 3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: // LCOV_EXCL_LINE
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief count digits
|
||||
|
||||
Count the number of decimal (base 10) digits for an input unsigned integer.
|
||||
|
||||
@param[in] x unsigned integer number to count its digits
|
||||
@return number of decimal digits
|
||||
*/
|
||||
inline unsigned int count_digits(number_unsigned_t x) noexcept
|
||||
{
|
||||
unsigned int n_digits = 1;
|
||||
for (;;)
|
||||
{
|
||||
if (x < 10)
|
||||
{
|
||||
return n_digits;
|
||||
}
|
||||
if (x < 100)
|
||||
{
|
||||
return n_digits + 1;
|
||||
}
|
||||
if (x < 1000)
|
||||
{
|
||||
return n_digits + 2;
|
||||
}
|
||||
if (x < 10000)
|
||||
{
|
||||
return n_digits + 3;
|
||||
}
|
||||
x = x / 10000u;
|
||||
n_digits += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief convert a byte to a uppercase hex representation
|
||||
* @param[in] byte byte to represent
|
||||
* @return representation ("00".."FF")
|
||||
*/
|
||||
static std::string hex_bytes(std::uint8_t byte)
|
||||
{
|
||||
std::string result = "FF";
|
||||
constexpr const char* nibble_to_hex = "0123456789ABCDEF";
|
||||
result[0] = nibble_to_hex[byte / 16];
|
||||
result[1] = nibble_to_hex[byte % 16];
|
||||
return result;
|
||||
}
|
||||
|
||||
// templates to avoid warnings about useless casts
|
||||
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
||||
bool is_negative_number(NumberType x)
|
||||
{
|
||||
return x < 0;
|
||||
}
|
||||
|
||||
template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >
|
||||
bool is_negative_number(NumberType /*unused*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief dump an integer
|
||||
|
||||
Dump a given integer to output stream @a o. Works internally with
|
||||
@a number_buffer.
|
||||
|
||||
@param[in] x integer number (signed or unsigned) to dump
|
||||
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
|
||||
*/
|
||||
template < typename NumberType, detail::enable_if_t <
|
||||
std::is_integral<NumberType>::value ||
|
||||
std::is_same<NumberType, number_unsigned_t>::value ||
|
||||
std::is_same<NumberType, number_integer_t>::value ||
|
||||
std::is_same<NumberType, binary_char_t>::value,
|
||||
int > = 0 >
|
||||
void dump_integer(NumberType x)
|
||||
{
|
||||
static constexpr std::array<std::array<char, 2>, 100> digits_to_99
|
||||
{
|
||||
{
|
||||
{{'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'}},
|
||||
}
|
||||
};
|
||||
|
||||
// special case for "0"
|
||||
if (x == 0)
|
||||
{
|
||||
o->write_character('0');
|
||||
return;
|
||||
}
|
||||
|
||||
// use a pointer to fill the buffer
|
||||
auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
|
||||
number_unsigned_t abs_value;
|
||||
|
||||
unsigned int n_chars{};
|
||||
|
||||
if (is_negative_number(x))
|
||||
{
|
||||
*buffer_ptr = '-';
|
||||
abs_value = remove_sign(static_cast<number_integer_t>(x));
|
||||
|
||||
// account one more byte for the minus sign
|
||||
n_chars = 1 + count_digits(abs_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
abs_value = static_cast<number_unsigned_t>(x);
|
||||
n_chars = count_digits(abs_value);
|
||||
}
|
||||
|
||||
// spare 1 byte for '\0'
|
||||
JSON_ASSERT(n_chars < number_buffer.size() - 1);
|
||||
|
||||
// jump to the end to generate the string from backward,
|
||||
// so we later avoid reversing the result
|
||||
buffer_ptr += n_chars;
|
||||
|
||||
// Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
|
||||
// See: https://www.youtube.com/watch?v=o4-CwDo2zpg
|
||||
while (abs_value >= 100)
|
||||
{
|
||||
const auto digits_index = static_cast<unsigned>((abs_value % 100));
|
||||
abs_value /= 100;
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][0];
|
||||
}
|
||||
|
||||
if (abs_value >= 10)
|
||||
{
|
||||
const auto digits_index = static_cast<unsigned>(abs_value);
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
*(--buffer_ptr) = static_cast<char>('0' + abs_value);
|
||||
}
|
||||
|
||||
o->write_characters(number_buffer.data(), n_chars);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief dump a floating-point number
|
||||
|
||||
Dump a given floating-point number to output stream @a o. Works internally
|
||||
with @a number_buffer.
|
||||
|
||||
@param[in] x floating-point number to dump
|
||||
*/
|
||||
void dump_float(number_float_t x)
|
||||
{
|
||||
// NaN / inf
|
||||
if (!std::isfinite(x))
|
||||
{
|
||||
o->write_characters("null", 4);
|
||||
return;
|
||||
}
|
||||
|
||||
// If number_float_t is an IEEE-754 single or double precision number,
|
||||
// use the Grisu2 algorithm to produce short numbers which are
|
||||
// guaranteed to round-trip, using strtof and strtod, resp.
|
||||
//
|
||||
// NB: The test below works if <long double> == <double>.
|
||||
static constexpr bool is_ieee_single_or_double
|
||||
= (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||
|
||||
(std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);
|
||||
|
||||
dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
|
||||
}
|
||||
|
||||
void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
|
||||
{
|
||||
auto* begin = number_buffer.data();
|
||||
auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
|
||||
|
||||
o->write_characters(begin, static_cast<size_t>(end - begin));
|
||||
}
|
||||
|
||||
void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
|
||||
{
|
||||
// get number of digits for a float -> text -> float round-trip
|
||||
static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
|
||||
|
||||
// the actual conversion
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
|
||||
|
||||
// negative value indicates an error
|
||||
JSON_ASSERT(len > 0);
|
||||
// check if buffer was large enough
|
||||
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
|
||||
|
||||
// erase thousands separator
|
||||
if (thousands_sep != '\0')
|
||||
{
|
||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
|
||||
std::fill(end, number_buffer.end(), '\0');
|
||||
JSON_ASSERT((end - number_buffer.begin()) <= len);
|
||||
len = (end - number_buffer.begin());
|
||||
}
|
||||
|
||||
// convert decimal point to '.'
|
||||
if (decimal_point != '\0' && decimal_point != '.')
|
||||
{
|
||||
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
|
||||
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
|
||||
if (dec_pos != number_buffer.end())
|
||||
{
|
||||
*dec_pos = '.';
|
||||
}
|
||||
}
|
||||
|
||||
o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
|
||||
|
||||
// determine if we need to append ".0"
|
||||
const bool value_is_int_like =
|
||||
std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
|
||||
[](char c)
|
||||
{
|
||||
return c == '.' || c == 'e';
|
||||
});
|
||||
|
||||
if (value_is_int_like)
|
||||
{
|
||||
o->write_characters(".0", 2);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief check whether a string is UTF-8 encoded
|
||||
|
||||
The function checks each byte of a string whether it is UTF-8 encoded. The
|
||||
result of the check is stored in the @a state parameter. The function must
|
||||
be called initially with state 0 (accept). State 1 means the string must
|
||||
be rejected, because the current byte is not allowed. If the string is
|
||||
completely processed, but the state is non-zero, the string ended
|
||||
prematurely; that is, the last byte indicated more bytes should have
|
||||
followed.
|
||||
|
||||
@param[in,out] state the state of the decoding
|
||||
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
|
||||
@param[in] byte next byte to decode
|
||||
@return new state
|
||||
|
||||
@note The function has been edited: a std::array is used.
|
||||
|
||||
@copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
*/
|
||||
static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
|
||||
{
|
||||
static const std::array<std::uint8_t, 400> utf8d =
|
||||
{
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
|
||||
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, // C0..DF
|
||||
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
|
||||
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
|
||||
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
|
||||
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
|
||||
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
|
||||
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
|
||||
}
|
||||
};
|
||||
|
||||
JSON_ASSERT(byte < utf8d.size());
|
||||
const std::uint8_t type = utf8d[byte];
|
||||
|
||||
codep = (state != UTF8_ACCEPT)
|
||||
? (byte & 0x3fu) | (codep << 6u)
|
||||
: (0xFFu >> type) & (byte);
|
||||
|
||||
const std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
|
||||
JSON_ASSERT(index < utf8d.size());
|
||||
state = utf8d[index];
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overload to make the compiler happy while it is instantiating
|
||||
* dump_integer for number_unsigned_t.
|
||||
* Must never be called.
|
||||
*/
|
||||
number_unsigned_t remove_sign(number_unsigned_t x)
|
||||
{
|
||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||
return x; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for dump_integer
|
||||
*
|
||||
* This function takes a negative signed integer and returns its absolute
|
||||
* value as unsigned integer. The plus/minus shuffling is necessary as we can
|
||||
* not directly remove the sign of an arbitrary signed integer as the
|
||||
* absolute values of INT_MIN and INT_MAX are usually not the same. See
|
||||
* #1708 for details.
|
||||
*/
|
||||
inline number_unsigned_t remove_sign(number_integer_t x) noexcept
|
||||
{
|
||||
JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
|
||||
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
|
||||
}
|
||||
|
||||
private:
|
||||
/// the output of the serializer
|
||||
output_adapter_t<char> o = nullptr;
|
||||
|
||||
/// a (hopefully) large enough character buffer
|
||||
std::array<char, 64> number_buffer{{}};
|
||||
|
||||
/// the locale
|
||||
const std::lconv* loc = nullptr;
|
||||
/// the locale's thousand separator character
|
||||
const char thousands_sep = '\0';
|
||||
/// the locale's decimal point character
|
||||
const char decimal_point = '\0';
|
||||
|
||||
/// string buffer
|
||||
std::array<char, 512> string_buffer{{}};
|
||||
|
||||
/// the indentation character
|
||||
const char indent_char;
|
||||
/// the indentation string
|
||||
string_t indent_string;
|
||||
|
||||
/// error_handler how to react on decoding errors
|
||||
const error_handler_t error_handler;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,146 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring> // strlen
|
||||
#include <string> // string
|
||||
#include <utility> // forward
|
||||
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
#include <nlohmann/detail/meta/detected.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
inline std::size_t concat_length()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::size_t concat_length(const char* cstr, const Args& ... rest);
|
||||
|
||||
template<typename StringType, typename... Args>
|
||||
inline std::size_t concat_length(const StringType& str, const Args& ... rest);
|
||||
|
||||
template<typename... Args>
|
||||
inline std::size_t concat_length(const char /*c*/, const Args& ... rest)
|
||||
{
|
||||
return 1 + concat_length(rest...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline std::size_t concat_length(const char* cstr, const Args& ... rest)
|
||||
{
|
||||
// cppcheck-suppress ignoredReturnValue
|
||||
return ::strlen(cstr) + concat_length(rest...);
|
||||
}
|
||||
|
||||
template<typename StringType, typename... Args>
|
||||
inline std::size_t concat_length(const StringType& str, const Args& ... rest)
|
||||
{
|
||||
return str.size() + concat_length(rest...);
|
||||
}
|
||||
|
||||
template<typename OutStringType>
|
||||
inline void concat_into(OutStringType& /*out*/)
|
||||
{}
|
||||
|
||||
template<typename StringType, typename Arg>
|
||||
using string_can_append = decltype(std::declval<StringType&>().append(std::declval < Arg && > ()));
|
||||
|
||||
template<typename StringType, typename Arg>
|
||||
using detect_string_can_append = is_detected<string_can_append, StringType, Arg>;
|
||||
|
||||
template<typename StringType, typename Arg>
|
||||
using string_can_append_op = decltype(std::declval<StringType&>() += std::declval < Arg && > ());
|
||||
|
||||
template<typename StringType, typename Arg>
|
||||
using detect_string_can_append_op = is_detected<string_can_append_op, StringType, Arg>;
|
||||
|
||||
template<typename StringType, typename Arg>
|
||||
using string_can_append_iter = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().begin(), std::declval<const Arg&>().end()));
|
||||
|
||||
template<typename StringType, typename Arg>
|
||||
using detect_string_can_append_iter = is_detected<string_can_append_iter, StringType, Arg>;
|
||||
|
||||
template<typename StringType, typename Arg>
|
||||
using string_can_append_data = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().data(), std::declval<const Arg&>().size()));
|
||||
|
||||
template<typename StringType, typename Arg>
|
||||
using detect_string_can_append_data = is_detected<string_can_append_data, StringType, Arg>;
|
||||
|
||||
template < typename OutStringType, typename Arg, typename... Args,
|
||||
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
|
||||
&& detect_string_can_append_op<OutStringType, Arg>::value, int > = 0 >
|
||||
inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest);
|
||||
|
||||
template < typename OutStringType, typename Arg, typename... Args,
|
||||
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
|
||||
&& !detect_string_can_append_op<OutStringType, Arg>::value
|
||||
&& detect_string_can_append_iter<OutStringType, Arg>::value, int > = 0 >
|
||||
inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
|
||||
|
||||
template < typename OutStringType, typename Arg, typename... Args,
|
||||
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
|
||||
&& !detect_string_can_append_op<OutStringType, Arg>::value
|
||||
&& !detect_string_can_append_iter<OutStringType, Arg>::value
|
||||
&& detect_string_can_append_data<OutStringType, Arg>::value, int > = 0 >
|
||||
inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
|
||||
|
||||
template<typename OutStringType, typename Arg, typename... Args,
|
||||
enable_if_t<detect_string_can_append<OutStringType, Arg>::value, int> = 0>
|
||||
inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest)
|
||||
{
|
||||
out.append(std::forward<Arg>(arg));
|
||||
concat_into(out, std::forward<Args>(rest)...);
|
||||
}
|
||||
|
||||
template < typename OutStringType, typename Arg, typename... Args,
|
||||
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
|
||||
&& detect_string_can_append_op<OutStringType, Arg>::value, int > >
|
||||
inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest)
|
||||
{
|
||||
out += std::forward<Arg>(arg);
|
||||
concat_into(out, std::forward<Args>(rest)...);
|
||||
}
|
||||
|
||||
template < typename OutStringType, typename Arg, typename... Args,
|
||||
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
|
||||
&& !detect_string_can_append_op<OutStringType, Arg>::value
|
||||
&& detect_string_can_append_iter<OutStringType, Arg>::value, int > >
|
||||
inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
|
||||
{
|
||||
out.append(arg.begin(), arg.end());
|
||||
concat_into(out, std::forward<Args>(rest)...);
|
||||
}
|
||||
|
||||
template < typename OutStringType, typename Arg, typename... Args,
|
||||
enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
|
||||
&& !detect_string_can_append_op<OutStringType, Arg>::value
|
||||
&& !detect_string_can_append_iter<OutStringType, Arg>::value
|
||||
&& detect_string_can_append_data<OutStringType, Arg>::value, int > >
|
||||
inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
|
||||
{
|
||||
out.append(arg.data(), arg.size());
|
||||
concat_into(out, std::forward<Args>(rest)...);
|
||||
}
|
||||
|
||||
template<typename OutStringType = std::string, typename... Args>
|
||||
inline OutStringType concat(Args && ... args)
|
||||
{
|
||||
OutStringType str;
|
||||
str.reserve(concat_length(args...));
|
||||
concat_into(str, std::forward<Args>(args)...);
|
||||
return str;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,72 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/*!
|
||||
@brief replace all occurrences of a substring by another string
|
||||
|
||||
@param[in,out] s the string to manipulate; changed so that all
|
||||
occurrences of @a f are replaced with @a t
|
||||
@param[in] f the substring to replace with @a t
|
||||
@param[in] t the string to replace @a f
|
||||
|
||||
@pre The search string @a f must not be empty. **This precondition is
|
||||
enforced with an assertion.**
|
||||
|
||||
@since version 2.0.0
|
||||
*/
|
||||
template<typename StringType>
|
||||
inline void replace_substring(StringType& s, const StringType& f,
|
||||
const StringType& t)
|
||||
{
|
||||
JSON_ASSERT(!f.empty());
|
||||
for (auto pos = s.find(f); // find first occurrence of f
|
||||
pos != StringType::npos; // make sure f was found
|
||||
s.replace(pos, f.size(), t), // replace with t, and
|
||||
pos = s.find(f, pos + t.size())) // find next occurrence of f
|
||||
{}
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief string escaping as described in RFC 6901 (Sect. 4)
|
||||
* @param[in] s string to escape
|
||||
* @return escaped string
|
||||
*
|
||||
* Note the order of escaping "~" to "~0" and "/" to "~1" is important.
|
||||
*/
|
||||
template<typename StringType>
|
||||
inline StringType escape(StringType s)
|
||||
{
|
||||
replace_substring(s, StringType{"~"}, StringType{"~0"});
|
||||
replace_substring(s, StringType{"/"}, StringType{"~1"});
|
||||
return s;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief string unescaping as described in RFC 6901 (Sect. 4)
|
||||
* @param[in] s string to unescape
|
||||
* @return unescaped string
|
||||
*
|
||||
* Note the order of escaping "~1" to "/" and "~0" to "~" is important.
|
||||
*/
|
||||
template<typename StringType>
|
||||
static void unescape(StringType& s)
|
||||
{
|
||||
replace_substring(s, StringType{"~1"}, StringType{"/"});
|
||||
replace_substring(s, StringType{"~0"}, StringType{"~"});
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -1,118 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint8_t
|
||||
#include <string> // string
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
#include <compare> // partial_ordering
|
||||
#endif
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
///////////////////////////
|
||||
// JSON type enumeration //
|
||||
///////////////////////////
|
||||
|
||||
/*!
|
||||
@brief the JSON type enumeration
|
||||
|
||||
This enumeration collects the different JSON types. It is internally used to
|
||||
distinguish the stored values, and the functions @ref basic_json::is_null(),
|
||||
@ref basic_json::is_object(), @ref basic_json::is_array(),
|
||||
@ref basic_json::is_string(), @ref basic_json::is_boolean(),
|
||||
@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
|
||||
@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
|
||||
@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
|
||||
@ref basic_json::is_structured() rely on it.
|
||||
|
||||
@note There are three enumeration entries (number_integer, number_unsigned, and
|
||||
number_float), because the library distinguishes these three types for numbers:
|
||||
@ref basic_json::number_unsigned_t is used for unsigned integers,
|
||||
@ref basic_json::number_integer_t is used for signed integers, and
|
||||
@ref basic_json::number_float_t is used for floating-point numbers or to
|
||||
approximate integers which do not fit in the limits of their respective type.
|
||||
|
||||
@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
|
||||
value with the default value for a given type
|
||||
|
||||
@since version 1.0.0
|
||||
*/
|
||||
enum class value_t : std::uint8_t
|
||||
{
|
||||
null, ///< null value
|
||||
object, ///< object (unordered set of name/value pairs)
|
||||
array, ///< array (ordered collection of values)
|
||||
string, ///< string value
|
||||
boolean, ///< boolean value
|
||||
number_integer, ///< number value (signed integer)
|
||||
number_unsigned, ///< number value (unsigned integer)
|
||||
number_float, ///< number value (floating-point)
|
||||
binary, ///< binary array (ordered collection of bytes)
|
||||
discarded ///< discarded by the parser callback function
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief comparison operator for JSON types
|
||||
|
||||
Returns an ordering that is similar to Python:
|
||||
- order: null < boolean < number < object < array < string < binary
|
||||
- furthermore, each type is not smaller than itself
|
||||
- discarded values are not comparable
|
||||
- binary is represented as a b"" string in python and directly comparable to a
|
||||
string; however, making a binary array directly comparable with a string would
|
||||
be surprising behavior in a JSON file.
|
||||
|
||||
@since version 1.0.0
|
||||
*/
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
|
||||
#else
|
||||
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
|
||||
#endif
|
||||
{
|
||||
static constexpr std::array<std::uint8_t, 9> order = {{
|
||||
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
|
||||
1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
|
||||
6 /* binary */
|
||||
}
|
||||
};
|
||||
|
||||
const auto l_index = static_cast<std::size_t>(lhs);
|
||||
const auto r_index = static_cast<std::size_t>(rhs);
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
if (l_index < order.size() && r_index < order.size())
|
||||
{
|
||||
return order[l_index] <=> order[r_index]; // *NOPAD*
|
||||
}
|
||||
return std::partial_ordering::unordered;
|
||||
#else
|
||||
return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
|
||||
#endif
|
||||
}
|
||||
|
||||
// GCC selects the built-in operator< over an operator rewritten from
|
||||
// a user-defined spaceship operator
|
||||
// Clang, MSVC, and ICC select the rewritten candidate
|
||||
// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
|
||||
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
|
||||
{
|
||||
return std::is_lt(lhs <=> rhs); // *NOPAD*
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
|
||||
#include <cstdint> // int64_t, uint64_t
|
||||
#include <map> // map
|
||||
#include <memory> // allocator
|
||||
#include <string> // string
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
|
||||
/*!
|
||||
@brief namespace for Niels Lohmann
|
||||
@see https://github.com/nlohmann
|
||||
@since version 1.0.0
|
||||
*/
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/*!
|
||||
@brief default JSONSerializer template argument
|
||||
|
||||
This serializer ignores the template arguments and uses ADL
|
||||
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
||||
for serialization.
|
||||
*/
|
||||
template<typename T = void, typename SFINAE = void>
|
||||
struct adl_serializer;
|
||||
|
||||
/// a class to store JSON values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||
template<template<typename U, typename V, typename... Args> class ObjectType =
|
||||
std::map,
|
||||
template<typename U, typename... Args> class ArrayType = std::vector,
|
||||
class StringType = std::string, class BooleanType = bool,
|
||||
class NumberIntegerType = std::int64_t,
|
||||
class NumberUnsignedType = std::uint64_t,
|
||||
class NumberFloatType = double,
|
||||
template<typename U> class AllocatorType = std::allocator,
|
||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||
adl_serializer,
|
||||
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||
class CustomBaseClass = void>
|
||||
class basic_json;
|
||||
|
||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||
template<typename RefStringType>
|
||||
class json_pointer;
|
||||
|
||||
/*!
|
||||
@brief default specialization
|
||||
@sa https://json.nlohmann.me/api/json/
|
||||
*/
|
||||
using json = basic_json<>;
|
||||
|
||||
/// @brief a minimal map-like container that preserves insertion order
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
||||
struct ordered_map;
|
||||
|
||||
/// @brief specialization that maintains the insertion order of object keys
|
||||
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||
using ordered_json = basic_json<nlohmann::ordered_map>;
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
@@ -1,359 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional> // equal_to, less
|
||||
#include <initializer_list> // initializer_list
|
||||
#include <iterator> // input_iterator_tag, iterator_traits
|
||||
#include <memory> // allocator
|
||||
#include <stdexcept> // for out_of_range
|
||||
#include <type_traits> // enable_if, is_convertible
|
||||
#include <utility> // pair
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/// ordered_map: a minimal map-like container that preserves insertion order
|
||||
/// for use within nlohmann::basic_json<ordered_map>
|
||||
template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<const Key, T>>>
|
||||
struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>
|
||||
{
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
using Container = std::vector<std::pair<const Key, T>, Allocator>;
|
||||
using iterator = typename Container::iterator;
|
||||
using const_iterator = typename Container::const_iterator;
|
||||
using size_type = typename Container::size_type;
|
||||
using value_type = typename Container::value_type;
|
||||
#ifdef JSON_HAS_CPP_14
|
||||
using key_compare = std::equal_to<>;
|
||||
#else
|
||||
using key_compare = std::equal_to<Key>;
|
||||
#endif
|
||||
|
||||
// Explicit constructors instead of `using Container::Container`
|
||||
// otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
|
||||
ordered_map() noexcept(noexcept(Container())) : Container{} {}
|
||||
explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
|
||||
template <class It>
|
||||
ordered_map(It first, It last, const Allocator& alloc = Allocator())
|
||||
: Container{first, last, alloc} {}
|
||||
ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
|
||||
: Container{init, alloc} {}
|
||||
|
||||
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
Container::emplace_back(key, std::forward<T>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
std::pair<iterator, bool> emplace(KeyType && key, T && t)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
T& operator[](const key_type& key)
|
||||
{
|
||||
return emplace(key, T{}).first->second;
|
||||
}
|
||||
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
T & operator[](KeyType && key)
|
||||
{
|
||||
return emplace(std::forward<KeyType>(key), T{}).first->second;
|
||||
}
|
||||
|
||||
const T& operator[](const key_type& key) const
|
||||
{
|
||||
return at(key);
|
||||
}
|
||||
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
const T & operator[](KeyType && key) const
|
||||
{
|
||||
return at(std::forward<KeyType>(key));
|
||||
}
|
||||
|
||||
T& at(const key_type& key)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
JSON_THROW(std::out_of_range("key not found"));
|
||||
}
|
||||
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
JSON_THROW(std::out_of_range("key not found"));
|
||||
}
|
||||
|
||||
const T& at(const key_type& key) const
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
JSON_THROW(std::out_of_range("key not found"));
|
||||
}
|
||||
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
JSON_THROW(std::out_of_range("key not found"));
|
||||
}
|
||||
|
||||
size_type erase(const key_type& key)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
// Since we cannot move const Keys, re-construct them in place
|
||||
for (auto next = it; ++next != this->end(); ++it)
|
||||
{
|
||||
it->~value_type(); // Destroy but keep allocation
|
||||
new (&*it) value_type{std::move(*next)};
|
||||
}
|
||||
Container::pop_back();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
// Since we cannot move const Keys, re-construct them in place
|
||||
for (auto next = it; ++next != this->end(); ++it)
|
||||
{
|
||||
it->~value_type(); // Destroy but keep allocation
|
||||
new (&*it) value_type{std::move(*next)};
|
||||
}
|
||||
Container::pop_back();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
iterator erase(iterator pos)
|
||||
{
|
||||
return erase(pos, std::next(pos));
|
||||
}
|
||||
|
||||
iterator erase(iterator first, iterator last)
|
||||
{
|
||||
if (first == last)
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
const auto elements_affected = std::distance(first, last);
|
||||
const auto offset = std::distance(Container::begin(), first);
|
||||
|
||||
// This is the start situation. We need to delete elements_affected
|
||||
// elements (3 in this example: e, f, g), and need to return an
|
||||
// iterator past the last deleted element (h in this example).
|
||||
// Note that offset is the distance from the start of the vector
|
||||
// to first. We will need this later.
|
||||
|
||||
// [ a, b, c, d, e, f, g, h, i, j ]
|
||||
// ^ ^
|
||||
// first last
|
||||
|
||||
// Since we cannot move const Keys, we re-construct them in place.
|
||||
// We start at first and re-construct (viz. copy) the elements from
|
||||
// the back of the vector. Example for first iteration:
|
||||
|
||||
// ,--------.
|
||||
// v | destroy e and re-construct with h
|
||||
// [ a, b, c, d, e, f, g, h, i, j ]
|
||||
// ^ ^
|
||||
// it it + elements_affected
|
||||
|
||||
for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it)
|
||||
{
|
||||
it->~value_type(); // destroy but keep allocation
|
||||
new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it
|
||||
}
|
||||
|
||||
// [ a, b, c, d, h, i, j, h, i, j ]
|
||||
// ^ ^
|
||||
// first last
|
||||
|
||||
// remove the unneeded elements at the end of the vector
|
||||
Container::resize(this->size() - static_cast<size_type>(elements_affected));
|
||||
|
||||
// [ a, b, c, d, h, i, j ]
|
||||
// ^ ^
|
||||
// first last
|
||||
|
||||
// first is now pointing past the last deleted element, but we cannot
|
||||
// use this iterator, because it may have been invalidated by the
|
||||
// resize call. Instead, we can return begin() + offset.
|
||||
return Container::begin() + offset;
|
||||
}
|
||||
|
||||
size_type count(const key_type& key) const
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
iterator find(const key_type& key)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return Container::end();
|
||||
}
|
||||
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return Container::end();
|
||||
}
|
||||
|
||||
const_iterator find(const key_type& key) const
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, key))
|
||||
{
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return Container::end();
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert( value_type&& value )
|
||||
{
|
||||
return emplace(value.first, std::move(value.second));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert( const value_type& value )
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if (m_compare(it->first, value.first))
|
||||
{
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
Container::push_back(value);
|
||||
return {--this->end(), true};
|
||||
}
|
||||
|
||||
template<typename InputIt>
|
||||
using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
|
||||
std::input_iterator_tag>::value>::type;
|
||||
|
||||
template<typename InputIt, typename = require_input_iter<InputIt>>
|
||||
void insert(InputIt first, InputIt last)
|
||||
{
|
||||
for (auto it = first; it != last; ++it)
|
||||
{
|
||||
insert(*it);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare();
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,158 +0,0 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#undef JSON_HEDLEY_ALWAYS_INLINE
|
||||
#undef JSON_HEDLEY_ARM_VERSION
|
||||
#undef JSON_HEDLEY_ARM_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_ARRAY_PARAM
|
||||
#undef JSON_HEDLEY_ASSUME
|
||||
#undef JSON_HEDLEY_BEGIN_C_DECLS
|
||||
#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_CLANG_HAS_BUILTIN
|
||||
#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_CLANG_HAS_EXTENSION
|
||||
#undef JSON_HEDLEY_CLANG_HAS_FEATURE
|
||||
#undef JSON_HEDLEY_CLANG_HAS_WARNING
|
||||
#undef JSON_HEDLEY_COMPCERT_VERSION
|
||||
#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_CONCAT
|
||||
#undef JSON_HEDLEY_CONCAT3
|
||||
#undef JSON_HEDLEY_CONCAT3_EX
|
||||
#undef JSON_HEDLEY_CONCAT_EX
|
||||
#undef JSON_HEDLEY_CONST
|
||||
#undef JSON_HEDLEY_CONSTEXPR
|
||||
#undef JSON_HEDLEY_CONST_CAST
|
||||
#undef JSON_HEDLEY_CPP_CAST
|
||||
#undef JSON_HEDLEY_CRAY_VERSION
|
||||
#undef JSON_HEDLEY_CRAY_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_C_DECL
|
||||
#undef JSON_HEDLEY_DEPRECATED
|
||||
#undef JSON_HEDLEY_DEPRECATED_FOR
|
||||
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
|
||||
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
|
||||
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
|
||||
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
|
||||
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
|
||||
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
|
||||
#undef JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#undef JSON_HEDLEY_DIAGNOSTIC_PUSH
|
||||
#undef JSON_HEDLEY_DMC_VERSION
|
||||
#undef JSON_HEDLEY_DMC_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_EMPTY_BASES
|
||||
#undef JSON_HEDLEY_EMSCRIPTEN_VERSION
|
||||
#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_END_C_DECLS
|
||||
#undef JSON_HEDLEY_FLAGS
|
||||
#undef JSON_HEDLEY_FLAGS_CAST
|
||||
#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_GCC_HAS_BUILTIN
|
||||
#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_GCC_HAS_EXTENSION
|
||||
#undef JSON_HEDLEY_GCC_HAS_FEATURE
|
||||
#undef JSON_HEDLEY_GCC_HAS_WARNING
|
||||
#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_GCC_VERSION
|
||||
#undef JSON_HEDLEY_GCC_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_GNUC_HAS_BUILTIN
|
||||
#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_GNUC_HAS_EXTENSION
|
||||
#undef JSON_HEDLEY_GNUC_HAS_FEATURE
|
||||
#undef JSON_HEDLEY_GNUC_HAS_WARNING
|
||||
#undef JSON_HEDLEY_GNUC_VERSION
|
||||
#undef JSON_HEDLEY_GNUC_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_HAS_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_HAS_BUILTIN
|
||||
#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
|
||||
#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
|
||||
#undef JSON_HEDLEY_HAS_EXTENSION
|
||||
#undef JSON_HEDLEY_HAS_FEATURE
|
||||
#undef JSON_HEDLEY_HAS_WARNING
|
||||
#undef JSON_HEDLEY_IAR_VERSION
|
||||
#undef JSON_HEDLEY_IAR_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_IBM_VERSION
|
||||
#undef JSON_HEDLEY_IBM_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_IMPORT
|
||||
#undef JSON_HEDLEY_INLINE
|
||||
#undef JSON_HEDLEY_INTEL_CL_VERSION
|
||||
#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_INTEL_VERSION
|
||||
#undef JSON_HEDLEY_INTEL_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_IS_CONSTANT
|
||||
#undef JSON_HEDLEY_IS_CONSTEXPR_
|
||||
#undef JSON_HEDLEY_LIKELY
|
||||
#undef JSON_HEDLEY_MALLOC
|
||||
#undef JSON_HEDLEY_MCST_LCC_VERSION
|
||||
#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_MESSAGE
|
||||
#undef JSON_HEDLEY_MSVC_VERSION
|
||||
#undef JSON_HEDLEY_MSVC_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_NEVER_INLINE
|
||||
#undef JSON_HEDLEY_NON_NULL
|
||||
#undef JSON_HEDLEY_NO_ESCAPE
|
||||
#undef JSON_HEDLEY_NO_RETURN
|
||||
#undef JSON_HEDLEY_NO_THROW
|
||||
#undef JSON_HEDLEY_NULL
|
||||
#undef JSON_HEDLEY_PELLES_VERSION
|
||||
#undef JSON_HEDLEY_PELLES_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_PGI_VERSION
|
||||
#undef JSON_HEDLEY_PGI_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_PREDICT
|
||||
#undef JSON_HEDLEY_PRINTF_FORMAT
|
||||
#undef JSON_HEDLEY_PRIVATE
|
||||
#undef JSON_HEDLEY_PUBLIC
|
||||
#undef JSON_HEDLEY_PURE
|
||||
#undef JSON_HEDLEY_REINTERPRET_CAST
|
||||
#undef JSON_HEDLEY_REQUIRE
|
||||
#undef JSON_HEDLEY_REQUIRE_CONSTEXPR
|
||||
#undef JSON_HEDLEY_REQUIRE_MSG
|
||||
#undef JSON_HEDLEY_RESTRICT
|
||||
#undef JSON_HEDLEY_RETURNS_NON_NULL
|
||||
#undef JSON_HEDLEY_SENTINEL
|
||||
#undef JSON_HEDLEY_STATIC_ASSERT
|
||||
#undef JSON_HEDLEY_STATIC_CAST
|
||||
#undef JSON_HEDLEY_STRINGIFY
|
||||
#undef JSON_HEDLEY_STRINGIFY_EX
|
||||
#undef JSON_HEDLEY_SUNPRO_VERSION
|
||||
#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_TINYC_VERSION
|
||||
#undef JSON_HEDLEY_TINYC_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_TI_ARMCL_VERSION
|
||||
#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_TI_CL2000_VERSION
|
||||
#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_TI_CL430_VERSION
|
||||
#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_TI_CL6X_VERSION
|
||||
#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_TI_CL7X_VERSION
|
||||
#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_TI_CLPRU_VERSION
|
||||
#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_TI_VERSION
|
||||
#undef JSON_HEDLEY_TI_VERSION_CHECK
|
||||
#undef JSON_HEDLEY_UNAVAILABLE
|
||||
#undef JSON_HEDLEY_UNLIKELY
|
||||
#undef JSON_HEDLEY_UNPREDICTABLE
|
||||
#undef JSON_HEDLEY_UNREACHABLE
|
||||
#undef JSON_HEDLEY_UNREACHABLE_RETURN
|
||||
#undef JSON_HEDLEY_VERSION
|
||||
#undef JSON_HEDLEY_VERSION_DECODE_MAJOR
|
||||
#undef JSON_HEDLEY_VERSION_DECODE_MINOR
|
||||
#undef JSON_HEDLEY_VERSION_DECODE_REVISION
|
||||
#undef JSON_HEDLEY_VERSION_ENCODE
|
||||
#undef JSON_HEDLEY_WARNING
|
||||
#undef JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
|
||||
#undef JSON_HEDLEY_FALL_THROUGH
|
||||
125
sample/sample.cc
125
sample/sample.cc
@@ -4,9 +4,22 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// ***************************************************************
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <ArffFiles.hpp>
|
||||
#include <CPPFImdlp.h>
|
||||
#include <bayesnet/ensembles/XBAODE.h>
|
||||
#include <fimdlp/CPPFImdlp.h>
|
||||
#include <bayesnet/classifiers/TANLd.h>
|
||||
#include <bayesnet/classifiers/KDBLd.h>
|
||||
#include <bayesnet/ensembles/AODELd.h>
|
||||
|
||||
torch::Tensor matrix2tensor(const std::vector<std::vector<float>>& matrix)
|
||||
{
|
||||
auto tensor = torch::empty({ static_cast<int>(matrix.size()), static_cast<int>(matrix[0].size()) }, torch::kFloat32);
|
||||
for (int i = 0; i < matrix.size(); ++i) {
|
||||
tensor.index_put_({ i, "..." }, torch::tensor(matrix[i], torch::kFloat32));
|
||||
}
|
||||
return tensor;
|
||||
}
|
||||
|
||||
std::vector<mdlp::labels_t> discretizeDataset(std::vector<mdlp::samples_t>& X, mdlp::labels_t& y)
|
||||
{
|
||||
@@ -19,63 +32,89 @@ std::vector<mdlp::labels_t> discretizeDataset(std::vector<mdlp::samples_t>& X, m
|
||||
}
|
||||
return Xd;
|
||||
}
|
||||
tuple<torch::Tensor, torch::Tensor, std::vector<std::string>, std::string, map<std::string, std::vector<int>>> loadDataset(const std::string& name, bool class_last)
|
||||
std::tuple<torch::Tensor, torch::Tensor, std::vector<std::string>, std::string> loadArff(const std::string& name, bool class_last)
|
||||
{
|
||||
auto handler = ArffFiles();
|
||||
handler.load(name, class_last);
|
||||
// Get Dataset X, y
|
||||
std::vector<mdlp::samples_t>& X = handler.getX();
|
||||
mdlp::labels_t& y = handler.getY();
|
||||
// Get className & Features
|
||||
auto className = handler.getClassName();
|
||||
std::vector<mdlp::samples_t> X = handler.getX();
|
||||
mdlp::labels_t y = handler.getY();
|
||||
std::vector<std::string> features;
|
||||
auto attributes = handler.getAttributes();
|
||||
transform(attributes.begin(), attributes.end(), back_inserter(features), [](const auto& pair) { return pair.first; });
|
||||
torch::Tensor Xd;
|
||||
auto states = map<std::string, std::vector<int>>();
|
||||
auto Xr = discretizeDataset(X, y);
|
||||
Xd = torch::zeros({ static_cast<int>(Xr.size()), static_cast<int>(Xr[0].size()) }, torch::kInt32);
|
||||
for (int i = 0; i < features.size(); ++i) {
|
||||
states[features[i]] = std::vector<int>(*max_element(Xr[i].begin(), Xr[i].end()) + 1);
|
||||
auto item = states.at(features[i]);
|
||||
iota(begin(item), end(item), 0);
|
||||
Xd.index_put_({ i, "..." }, torch::tensor(Xr[i], torch::kInt32));
|
||||
}
|
||||
states[className] = std::vector<int>(*max_element(y.begin(), y.end()) + 1);
|
||||
iota(begin(states.at(className)), end(states.at(className)), 0);
|
||||
return { Xd, torch::tensor(y, torch::kInt32), features, className, states };
|
||||
auto Xt = matrix2tensor(X);
|
||||
auto yt = torch::tensor(y, torch::kInt32);
|
||||
return { Xt, yt, features, handler.getClassName() };
|
||||
}
|
||||
// tuple<torch::Tensor, torch::Tensor, std::vector<std::string>, std::string, map<std::string, std::vector<int>>> loadDataset(const std::string& name, bool class_last)
|
||||
// {
|
||||
// auto [X, y, features, className] = loadArff(name, class_last);
|
||||
// // Discretize the dataset
|
||||
// torch::Tensor Xd;
|
||||
// auto states = map<std::string, std::vector<int>>();
|
||||
// // Fill the class states
|
||||
// states[className] = std::vector<int>(*max_element(y.begin(), y.end()) + 1);
|
||||
// iota(begin(states.at(className)), end(states.at(className)), 0);
|
||||
// auto Xr = discretizeDataset(X, y);
|
||||
// Xd = torch::zeros({ static_cast<int>(Xr.size()), static_cast<int>(Xr[0].size()) }, torch::kInt32);
|
||||
// for (int i = 0; i < features.size(); ++i) {
|
||||
// states[features[i]] = std::vector<int>(*max_element(Xr[i].begin(), Xr[i].end()) + 1);
|
||||
// auto item = states.at(features[i]);
|
||||
// iota(begin(item), end(item), 0);
|
||||
// Xd.index_put_({ i, "..." }, torch::tensor(Xr[i], torch::kInt32));
|
||||
// }
|
||||
// auto yt = torch::tensor(y, torch::kInt32);
|
||||
// return { Xd, yt, features, className, states };
|
||||
// }
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
std::cerr << "Usage: " << argv[0] << " <file_name>" << std::endl;
|
||||
if (argc < 3) {
|
||||
std::cerr << "Usage: " << argv[0] << " <arff_file_name> <model>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::string file_name = argv[1];
|
||||
torch::Tensor X, y;
|
||||
std::vector<std::string> features;
|
||||
std::string className;
|
||||
map<std::string, std::vector<int>> states;
|
||||
auto clf = bayesnet::XBAODE(); // false for not using voting in predict
|
||||
std::cout << "Library version: " << clf.getVersion() << std::endl;
|
||||
tie(X, y, features, className, states) = loadDataset(file_name, true);
|
||||
torch::Tensor weights = torch::full({ X.size(1) }, 15, torch::kDouble);
|
||||
torch::Tensor dataset;
|
||||
try {
|
||||
auto yresized = torch::transpose(y.view({ y.size(0), 1 }), 0, 1);
|
||||
dataset = torch::cat({ X, yresized }, 0);
|
||||
std::string model_name = argv[2];
|
||||
std::map<std::string, bayesnet::Classifier*> models{ {"TANLd", new bayesnet::TANLd()}, {"KDBLd", new bayesnet::KDBLd(2)}, {"AODELd", new bayesnet::AODELd() }
|
||||
};
|
||||
if (models.find(model_name) == models.end()) {
|
||||
std::cerr << "Model not found: " << model_name << std::endl;
|
||||
std::cerr << "Available models: ";
|
||||
for (const auto& model : models) {
|
||||
std::cerr << model.first << " ";
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::stringstream oss;
|
||||
oss << "* Error in X and y dimensions *\n";
|
||||
oss << "X dimensions: " << dataset.sizes() << "\n";
|
||||
oss << "y dimensions: " << y.sizes();
|
||||
throw std::runtime_error(oss.str());
|
||||
std::cerr << std::endl;
|
||||
return 1;
|
||||
}
|
||||
auto clf = models[model_name];
|
||||
std::cout << "Library version: " << clf->getVersion() << std::endl;
|
||||
// auto [X, y, features, className, states] = loadDataset(file_name, true);
|
||||
auto [Xt, yt, features, className] = loadArff(file_name, true);
|
||||
std::map<std::string, std::vector<int>> states;
|
||||
// int m = Xt.size(1);
|
||||
// auto weights = torch::full({ m }, 1 / m, torch::kDouble);
|
||||
// auto dataset = buildDataset(Xv, yv);
|
||||
// try {
|
||||
// auto yresized = torch::transpose(y.view({ y.size(0), 1 }), 0, 1);
|
||||
// dataset = torch::cat({ X, yresized }, 0);
|
||||
// }
|
||||
// catch (const std::exception& e) {
|
||||
// std::stringstream oss;
|
||||
// oss << "* Error in X and y dimensions *\n";
|
||||
// oss << "X dimensions: " << dataset.sizes() << "\n";
|
||||
// oss << "y dimensions: " << y.sizes();
|
||||
// throw std::runtime_error(oss.str());
|
||||
// }
|
||||
clf->fit(Xt, yt, features, className, states, bayesnet::Smoothing_t::ORIGINAL);
|
||||
auto total = yt.size(0);
|
||||
auto y_proba = clf->predict_proba(Xt);
|
||||
auto y_pred = y_proba.argmax(1);
|
||||
auto accuracy_value = (y_pred == yt).sum().item<float>() / total;
|
||||
auto score = clf->score(Xt, yt);
|
||||
std::cout << "File: " << file_name << " Model: " << model_name << " score: " << score << " Computed accuracy: " << accuracy_value << std::endl;
|
||||
for (const auto clf : models) {
|
||||
delete clf.second;
|
||||
}
|
||||
clf.fit(dataset, features, className, states, weights, bayesnet::Smoothing_t::LAPLACE);
|
||||
auto score = clf.score(X, y);
|
||||
std::cout << "File: " << file_name << " Model: BoostAODE score: " << score << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// ***************************************************************
|
||||
|
||||
#include <ArffFiles.hpp>
|
||||
#include <CPPFImdlp.h>
|
||||
#include <ArffFiles/ArffFiles.hpp>
|
||||
#include <fimdlp/CPPFImdlp.h>
|
||||
#include <bayesnet/ensembles/BoostAODE.h>
|
||||
#include <bayesnet/classifiers/XSPODE.h>
|
||||
|
||||
|
||||
21
sample/vcpkg-configuration.json
Normal file
21
sample/vcpkg-configuration.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"default-registry": {
|
||||
"kind": "git",
|
||||
"baseline": "760bfd0c8d7c89ec640aec4df89418b7c2745605",
|
||||
"repository": "https://github.com/microsoft/vcpkg"
|
||||
},
|
||||
"registries": [
|
||||
{
|
||||
"kind": "git",
|
||||
"repository": "https://github.com/rmontanana/vcpkg-stash",
|
||||
"baseline": "1ea69243c0e8b0de77c9d1dd6e1d7593ae7f3627",
|
||||
"packages": [
|
||||
"arff-files",
|
||||
"bayesnet",
|
||||
"fimdlp",
|
||||
"folding",
|
||||
"libtorch-bin"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
33
sample/vcpkg.json
Normal file
33
sample/vcpkg.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "sample-project",
|
||||
"version-string": "0.1.0",
|
||||
"dependencies": [
|
||||
"arff-files",
|
||||
"fimdlp",
|
||||
"libtorch-bin",
|
||||
"folding",
|
||||
"nlohmann-json"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"name": "arff-files",
|
||||
"version": "1.1.0"
|
||||
},
|
||||
{
|
||||
"name": "fimdlp",
|
||||
"version": "2.0.1"
|
||||
},
|
||||
{
|
||||
"name": "libtorch-bin",
|
||||
"version": "2.7.0"
|
||||
},
|
||||
{
|
||||
"name": "bayesnet",
|
||||
"version": "1.1.1"
|
||||
},
|
||||
{
|
||||
"name": "folding",
|
||||
"version": "1.1.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
if(ENABLE_TESTING)
|
||||
include_directories(
|
||||
${BayesNet_SOURCE_DIR}/tests/lib/Files
|
||||
${BayesNet_SOURCE_DIR}/lib/folding
|
||||
${BayesNet_SOURCE_DIR}/lib/mdlp/src
|
||||
${BayesNet_SOURCE_DIR}/lib/log
|
||||
${BayesNet_SOURCE_DIR}/lib/json/include
|
||||
${BayesNet_SOURCE_DIR}
|
||||
${CMAKE_BINARY_DIR}/configured_files/include
|
||||
${nlohmann_json_INCLUDE_DIRS}
|
||||
)
|
||||
file(GLOB_RECURSE BayesNet_SOURCES "${BayesNet_SOURCE_DIR}/bayesnet/*.cc")
|
||||
file(GLOB_RECURSE BayesNet_SOURCES "${bayesnet_SOURCE_DIR}/bayesnet/*.cc")
|
||||
add_executable(TestBayesNet TestBayesNetwork.cc TestBayesNode.cc TestBayesClassifier.cc TestXSPnDE.cc TestXBA2DE.cc
|
||||
TestBayesModels.cc TestBayesMetrics.cc TestFeatureSelection.cc TestBoostAODE.cc TestXBAODE.cc TestA2DE.cc
|
||||
TestUtils.cc TestBayesEnsemble.cc TestModulesVersions.cc TestBoostA2DE.cc TestMST.cc TestXSPODE.cc ${BayesNet_SOURCES})
|
||||
target_link_libraries(TestBayesNet PUBLIC "${TORCH_LIBRARIES}" fimdlp PRIVATE Catch2::Catch2WithMain)
|
||||
target_link_libraries(TestBayesNet PUBLIC "${TORCH_LIBRARIES}" fimdlp::fimdlp PRIVATE Catch2::Catch2WithMain folding::folding)
|
||||
add_test(NAME BayesNetworkTest COMMAND TestBayesNet)
|
||||
add_test(NAME A2DE COMMAND TestBayesNet "[A2DE]")
|
||||
add_test(NAME BoostA2DE COMMAND TestBayesNet "[BoostA2DE]")
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "bayesnet/ensembles/AODELd.h"
|
||||
#include "bayesnet/ensembles/BoostAODE.h"
|
||||
|
||||
const std::string ACTUAL_VERSION = "1.0.7";
|
||||
const std::string ACTUAL_VERSION = "1.2.0";
|
||||
|
||||
TEST_CASE("Test Bayesian Classifiers score & version", "[Models]")
|
||||
{
|
||||
@@ -152,7 +152,7 @@ TEST_CASE("Get num features & num edges", "[Models]")
|
||||
|
||||
TEST_CASE("Model predict_proba", "[Models]")
|
||||
{
|
||||
std::string model = GENERATE("TAN", "SPODE", "BoostAODEproba", "BoostAODEvoting");
|
||||
std::string model = GENERATE("TAN", "SPODE", "BoostAODEproba", "BoostAODEvoting", "TANLd", "SPODELd", "KDBLd");
|
||||
auto res_prob_tan = std::vector<std::vector<double>>({ {0.00375671, 0.994457, 0.00178621},
|
||||
{0.00137462, 0.992734, 0.00589123},
|
||||
{0.00137462, 0.992734, 0.00589123},
|
||||
@@ -180,32 +180,70 @@ TEST_CASE("Model predict_proba", "[Models]")
|
||||
{0.0284828, 0.770524, 0.200993},
|
||||
{0.0213182, 0.857189, 0.121493},
|
||||
{0.00868436, 0.949494, 0.0418215} });
|
||||
auto res_prob_tanld = std::vector<std::vector<double>>({ {0.000544493, 0.995796, 0.00365992 },
|
||||
{0.000908092, 0.997268, 0.00182429 },
|
||||
{0.000908092, 0.997268, 0.00182429 },
|
||||
{0.000908092, 0.997268, 0.00182429 },
|
||||
{0.00228423, 0.994645, 0.00307078 },
|
||||
{0.00120539, 0.0666788, 0.932116 },
|
||||
{0.00361847, 0.979203, 0.017179 },
|
||||
{0.00483293, 0.985326, 0.00984064 },
|
||||
{0.000595606, 0.9977, 0.00170441 } });
|
||||
auto res_prob_spodeld = std::vector<std::vector<double>>({ {0.000908024, 0.993742, 0.00535024 },
|
||||
{0.00187726, 0.99167, 0.00645308 },
|
||||
{0.00187726, 0.99167, 0.00645308 },
|
||||
{0.00187726, 0.99167, 0.00645308 },
|
||||
{0.00287539, 0.993736, 0.00338846 },
|
||||
{0.00294402, 0.268495, 0.728561 },
|
||||
{0.0132381, 0.873282, 0.113479 },
|
||||
{0.0159412, 0.969228, 0.0148308 },
|
||||
{0.00203487, 0.989762, 0.00820356 } });
|
||||
auto res_prob_kdbld = std::vector<std::vector<double>>({ {0.000738981, 0.997208, 0.00205272 },
|
||||
{0.00087708, 0.996687, 0.00243633 },
|
||||
{0.00087708, 0.996687, 0.00243633 },
|
||||
{0.00087708, 0.996687, 0.00243633 },
|
||||
{0.000738981, 0.997208, 0.00205272 },
|
||||
{0.00512442, 0.0455504, 0.949325 },
|
||||
{0.0023632, 0.976631, 0.0210063 },
|
||||
{0.00189194, 0.992853, 0.00525538 },
|
||||
{0.00189194, 0.992853, 0.00525538, } });
|
||||
auto res_prob_voting = std::vector<std::vector<double>>(
|
||||
{ {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0} });
|
||||
std::map<std::string, std::vector<std::vector<double>>> res_prob{ {"TAN", res_prob_tan},
|
||||
{"SPODE", res_prob_spode},
|
||||
{"BoostAODEproba", res_prob_baode},
|
||||
{"BoostAODEvoting", res_prob_voting} };
|
||||
{"BoostAODEvoting", res_prob_voting},
|
||||
{"TANLd", res_prob_tanld},
|
||||
{"SPODELd", res_prob_spodeld},
|
||||
{"KDBLd", res_prob_kdbld} };
|
||||
std::map<std::string, bayesnet::BaseClassifier*> models{ {"TAN", new bayesnet::TAN()},
|
||||
{"SPODE", new bayesnet::SPODE(0)},
|
||||
{"BoostAODEproba", new bayesnet::BoostAODE(false)},
|
||||
{"BoostAODEvoting", new bayesnet::BoostAODE(true)} };
|
||||
{"BoostAODEvoting", new bayesnet::BoostAODE(true)},
|
||||
{"TANLd", new bayesnet::TANLd()},
|
||||
{"SPODELd", new bayesnet::SPODELd(0)},
|
||||
{"KDBLd", new bayesnet::KDBLd(2)} };
|
||||
int init_index = 78;
|
||||
auto raw = RawDatasets("iris", true);
|
||||
|
||||
SECTION("Test " + model + " predict_proba")
|
||||
{
|
||||
auto ld_model = model.substr(model.length() - 2) == "Ld";
|
||||
auto discretize = !ld_model;
|
||||
auto raw = RawDatasets("iris", discretize);
|
||||
auto clf = models[model];
|
||||
clf->fit(raw.Xv, raw.yv, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
auto y_pred_proba = clf->predict_proba(raw.Xv);
|
||||
clf->fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
auto yt_pred_proba = clf->predict_proba(raw.Xt);
|
||||
auto y_pred = clf->predict(raw.Xv);
|
||||
auto yt_pred = clf->predict(raw.Xt);
|
||||
REQUIRE(y_pred.size() == yt_pred.size(0));
|
||||
std::vector<int> y_pred;
|
||||
std::vector<std::vector<double>> y_pred_proba;
|
||||
if (!ld_model) {
|
||||
y_pred = clf->predict(raw.Xv);
|
||||
y_pred_proba = clf->predict_proba(raw.Xv);
|
||||
REQUIRE(y_pred.size() == y_pred_proba.size());
|
||||
REQUIRE(y_pred.size() == yt_pred.size(0));
|
||||
REQUIRE(y_pred.size() == yt_pred_proba.size(0));
|
||||
REQUIRE(y_pred.size() == raw.yv.size());
|
||||
REQUIRE(y_pred_proba[0].size() == 3);
|
||||
REQUIRE(y_pred.size() == raw.yv.size());
|
||||
REQUIRE(yt_pred_proba.size(1) == y_pred_proba[0].size());
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
auto maxElem = max_element(y_pred_proba[i].begin(), y_pred_proba[i].end());
|
||||
@@ -226,6 +264,17 @@ TEST_CASE("Model predict_proba", "[Models]")
|
||||
Catch::Approx(yt_pred_proba[i + init_index][j].item<double>()).epsilon(raw.epsilon));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check predict_proba values for vectors and tensors
|
||||
auto predictedClasses = yt_pred_proba.argmax(1);
|
||||
for (int i = 0; i < 9; i++) {
|
||||
REQUIRE(predictedClasses[i].item<int>() == yt_pred[i].item<int>());
|
||||
for (int j = 0; j < 3; j++) {
|
||||
REQUIRE(res_prob[model][i][j] ==
|
||||
Catch::Approx(yt_pred_proba[i + init_index][j].item<double>()).epsilon(raw.epsilon));
|
||||
}
|
||||
}
|
||||
}
|
||||
delete clf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,11 @@ TEST_CASE("Feature_select IWSS", "[BoostA2DE]")
|
||||
auto clf = bayesnet::BoostA2DE();
|
||||
clf.setHyperparameters({ {"select_features", "IWSS"}, {"threshold", 0.5 } });
|
||||
clf.fit(raw.Xv, raw.yv, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
REQUIRE(clf.getNumberOfNodes() == 140);
|
||||
REQUIRE(clf.getNumberOfEdges() == 294);
|
||||
REQUIRE(clf.getNotes().size() == 4);
|
||||
REQUIRE(clf.getNotes()[0] == "Used features in initialization: 4 of 9 with IWSS");
|
||||
REQUIRE(clf.getNotes()[1] == "Convergence threshold reached & 15 models eliminated");
|
||||
REQUIRE(clf.getNotes()[2] == "Pairs not used in train: 2");
|
||||
REQUIRE(clf.getNotes()[3] == "Number of models: 14");
|
||||
REQUIRE(clf.getNumberOfNodes() == 360);
|
||||
REQUIRE(clf.getNumberOfEdges() == 756);
|
||||
REQUIRE(clf.getNotes().size() == 2);
|
||||
REQUIRE(clf.getNotes()[0] == "Used features in initialization: 9 of 9 with IWSS");
|
||||
REQUIRE(clf.getNotes()[1] == "Number of models: 36");
|
||||
}
|
||||
TEST_CASE("Feature_select FCBF", "[BoostA2DE]")
|
||||
{
|
||||
@@ -64,15 +62,15 @@ TEST_CASE("Test used features in train note and score", "[BoostA2DE]")
|
||||
{"select_features","CFS"},
|
||||
});
|
||||
clf.fit(raw.Xv, raw.yv, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
REQUIRE(clf.getNumberOfNodes() == 144);
|
||||
REQUIRE(clf.getNumberOfEdges() == 288);
|
||||
REQUIRE(clf.getNumberOfNodes() == 189);
|
||||
REQUIRE(clf.getNumberOfEdges() == 378);
|
||||
REQUIRE(clf.getNotes().size() == 2);
|
||||
REQUIRE(clf.getNotes()[0] == "Used features in initialization: 6 of 8 with CFS");
|
||||
REQUIRE(clf.getNotes()[1] == "Number of models: 16");
|
||||
REQUIRE(clf.getNotes()[0] == "Used features in initialization: 7 of 8 with CFS");
|
||||
REQUIRE(clf.getNotes()[1] == "Number of models: 21");
|
||||
auto score = clf.score(raw.Xv, raw.yv);
|
||||
auto scoret = clf.score(raw.Xt, raw.yt);
|
||||
REQUIRE(score == Catch::Approx(0.856771).epsilon(raw.epsilon));
|
||||
REQUIRE(scoret == Catch::Approx(0.856771).epsilon(raw.epsilon));
|
||||
REQUIRE(score == Catch::Approx(0.85546875f).epsilon(raw.epsilon));
|
||||
REQUIRE(scoret == Catch::Approx(0.85546875f).epsilon(raw.epsilon));
|
||||
}
|
||||
TEST_CASE("Voting vs proba", "[BoostA2DE]")
|
||||
{
|
||||
@@ -162,7 +160,7 @@ TEST_CASE("Bisection Best", "[BoostA2DE]")
|
||||
{"maxTolerance", 3},
|
||||
{"convergence", true},
|
||||
{"block_update", false},
|
||||
{"convergence_best", false},
|
||||
{"convergence_best", true},
|
||||
});
|
||||
clf.fit(raw.X_train, raw.y_train, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
REQUIRE(clf.getNumberOfNodes() == 480);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user