aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2021-11-15 12:51:20 +0100
committerToni Uhlig <matzeton@googlemail.com>2021-11-15 12:51:20 +0100
commit662f5a771f3807bf7ebd5a3989230d5d0a26659d (patch)
tree51cf82d5f470a0f4a9613bccbb3da3492f9eee34
parent514cb71a6a3e116c229c5dc874369f8632530dc7 (diff)
Squashed 'deps/inja/' changes from 811e173..eb04bfc
eb04bfc clang-format 81cfa14 init LiteralNode with string view, code cleaning 2da715a parse_filename without json 623c267 make_result and code cleaning 1206913 rename json -> data d38c07e clang 5 minimum a067a8b make all tests C++17 098de66 inja_test C++17 173c1f5 fix C++17 2239e23 remove string view polyfill fb55f2e update minimum compiler 751d27d fix MIT license 635e1fb change readme example 5a4ac92 update single include d038c53 set .cpp in readme cf71b54 add include callback 9b9dd96 introduce INJA_DATA_TYPE 12a2c9b unify json data type 4505fd0 add tests for assignments 2d51507 set version 3.3 2ba5783 Merge branch 'master' of github.com:pantor/inja 99b85d6 remove headers in files, add central header 14f0d38 Fixed an issue with round (#207) c70fd58 Merge branch 'master' of github.com:pantor/inja 8a9aee1 add jinja python test script 91c93bf add join function (#204) 9cf7db8 add warnings for benchmark, fix for inja_throw e91a2fd #ifndef INJA_THROW 8d65633 add warnings for clang and fix them eaec58d update nlohmann/json to master 4ccadd5 update to json=3.9.1, doctest=2.4.6 83feb26 update single include 4e90947 at function for objects b473873 compactify assignments readme 798a0b9 Add support for setting subobjects via JSON pointer in set statements. (#202) 86f38f0 update single include d9ad8d1 whitespace control for comments 95af782 Add Child Inheritance Feature (#198) b4b9d8d Fix build warnings for Clang and MSVC (#197) b14f8a1 add ci for gcc 10, 11 06ff271 add no excpetion test 2491980 Fix inclusion in translation units with exceptions disabled. (#196) 4d5a7d1 fix single include eac2162 fix include of in-memory templates ca3c7a0 add test for short circuit evaluation 389c1d6 short circuit evaluation git-subtree-dir: deps/inja git-subtree-split: eb04bfc7d3cf0a5fbd0ed37a88d064230d0af377
-rw-r--r--.clang-format11
-rw-r--r--.github/workflows/ci.yml79
-rw-r--r--.github/workflows/documentation.yml2
-rw-r--r--.github/workflows/single-include.yml6
-rw-r--r--.gitignore3
-rw-r--r--CMakeLists.txt36
-rw-r--r--LICENSE31
-rw-r--r--README.md104
-rw-r--r--doc/Doxyfile2
-rw-r--r--include/inja/config.hpp11
-rw-r--r--include/inja/environment.hpp87
-rw-r--r--include/inja/exceptions.hpp21
-rw-r--r--include/inja/function_storage.hpp86
-rw-r--r--include/inja/inja.hpp48
-rw-r--r--include/inja/lexer.hpp77
-rw-r--r--include/inja/node.hpp262
-rw-r--r--include/inja/parser.hpp273
-rw-r--r--include/inja/renderer.hpp524
-rw-r--r--include/inja/statistics.hpp37
-rw-r--r--include/inja/string_view.hpp1416
-rw-r--r--include/inja/template.hpp8
-rw-r--r--include/inja/token.hpp11
-rw-r--r--include/inja/utils.hpp40
-rw-r--r--single_include/inja/inja.hpp3570
-rw-r--r--test/benchmark.cpp20
-rw-r--r--test/data/html-extend/base.txt11
-rw-r--r--test/data/html-extend/data.json12
-rw-r--r--test/data/html-extend/inter.txt3
-rw-r--r--test/data/html-extend/result.txt11
-rw-r--r--test/data/html-extend/template.txt7
-rwxr-xr-xtest/data/include-both.txt1
-rw-r--r--test/jinja.py11
-rw-r--r--test/templates/example.txt1
-rw-r--r--test/test-files.cpp35
-rw-r--r--test/test-functions.cpp51
-rw-r--r--test/test-renderer.cpp79
-rw-r--r--test/test-units.cpp10
-rw-r--r--test/test.cpp2
-rw-r--r--third_party/include/doctest/LICENSE.txt2
-rw-r--r--third_party/include/doctest/doctest.h1619
-rw-r--r--third_party/include/nlohmann/LICENSE.MIT2
-rw-r--r--third_party/include/nlohmann/json.hpp8698
42 files changed, 8552 insertions, 8768 deletions
diff --git a/.clang-format b/.clang-format
index 5486ac5..3a48294 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,7 +1,14 @@
---
+BasedOnStyle: LLVM
+IndentWidth: 2
+ColumnLimit: 160
+
+---
Language: Cpp
-BasedOnStyle: LLVM
-ColumnLimit: 120
SpaceBeforeCpp11BracedList: true
+PointerAlignment: Left
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortBlocksOnASingleLine: Empty
+SpaceBeforeCtorInitializerColon: false
...
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c5909c3..c499221 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,24 +16,19 @@ jobs:
fail-fast: false
matrix:
name: [
- ubuntu-18.04-gcc-4.9,
- ubuntu-18.04-gcc-5,
- ubuntu-18.04-gcc-6,
ubuntu-18.04-gcc-7,
ubuntu-18.04-gcc-8,
ubuntu-18.04-gcc-9,
- ubuntu-18.04-clang-3.5,
- ubuntu-18.04-clang-3.8,
- ubuntu-18.04-clang-4.0,
+ ubuntu-20.04-gcc-10,
+ ubuntu-20.04-gcc-11,
ubuntu-18.04-clang-5.0,
ubuntu-18.04-clang-6.0,
ubuntu-18.04-clang-7,
ubuntu-18.04-clang-8,
ubuntu-20.04-clang-9,
- windows-2016-cl,
- windows-2016-clang-cl,
- windows-2016-clang,
- windows-2016-gcc,
+ ubuntu-20.04-clang-10,
+ ubuntu-20.04-clang-11,
+ ubuntu-20.04-clang-11-no-exceptions,
windows-2019-cl,
windows-2019-clang-cl,
windows-2019-clang,
@@ -46,21 +41,6 @@ jobs:
]
include:
- - name: ubuntu-18.04-gcc-4.9
- os: ubuntu-18.04
- compiler: gcc
- version: "4.9"
-
- - name: ubuntu-18.04-gcc-5
- os: ubuntu-18.04
- compiler: gcc
- version: "5"
-
- - name: ubuntu-18.04-gcc-6
- os: ubuntu-18.04
- compiler: gcc
- version: "6"
-
- name: ubuntu-18.04-gcc-7
os: ubuntu-18.04
compiler: gcc
@@ -76,20 +56,15 @@ jobs:
compiler: gcc
version: "9"
- - name: ubuntu-18.04-clang-3.5
- os: ubuntu-18.04
- compiler: clang
- version: "3.5"
-
- - name: ubuntu-18.04-clang-3.8
- os: ubuntu-18.04
- compiler: clang
- version: "3.8"
+ - name: ubuntu-20.04-gcc-10
+ os: ubuntu-20.04
+ compiler: gcc
+ version: "10"
- - name: ubuntu-18.04-clang-4.0
- os: ubuntu-18.04
- compiler: clang
- version: "4.0"
+ - name: ubuntu-20.04-gcc-11
+ os: ubuntu-20.04
+ compiler: gcc
+ version: "11"
- name: ubuntu-18.04-clang-5.0
os: ubuntu-18.04
@@ -116,26 +91,26 @@ jobs:
compiler: clang
version: "9"
- - name: windows-2016-cl
- os: windows-2016
- compiler: cl
+ - name: ubuntu-20.04-clang-10
+ os: ubuntu-20.04
+ compiler: clang
+ version: "10"
- - name: windows-2016-clang-cl
- os: windows-2016
- compiler: clang-cl
+ - name: ubuntu-20.04-clang-11
+ os: ubuntu-20.04
+ compiler: clang
+ version: "11"
- - name: windows-2016-clang
- os: windows-2016
+ - name: ubuntu-20.04-clang-11-no-exceptions
+ os: ubuntu-20.04
compiler: clang
+ version: "11"
+ cmake_vars: "-DCMAKE_CXX_FLAGS=-fno-exceptions -DBUILD_TESTING=OFF -DBUILD_BENCHMARK=ON"
- name: windows-2019-cl
os: windows-2019
compiler: cl
- - name: windows-2016-gcc
- os: windows-2016
- compiler: gcc
-
- name: windows-2019-clang-cl
os: windows-2019
compiler: clang-cl
@@ -174,7 +149,7 @@ jobs:
version: "12.2"
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v2
- name: Install (Linux)
if: runner.os == 'Linux'
@@ -256,6 +231,6 @@ jobs:
- name: Build & Test Release
run: |
cmake -E remove_directory build
- cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
+ cmake -B build -S . -DCMAKE_BUILD_TYPE=Release ${{ matrix.cmake_vars }}
cmake --build build -j2
cd build && ctest -j2 --output-on-failure
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index cddd438..e32cfeb 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -9,7 +9,7 @@ jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@master
+ - uses: actions/checkout@v2
- name: dependencies
env:
diff --git a/.github/workflows/single-include.yml b/.github/workflows/single-include.yml
index 44a5a66..a7b0a7f 100644
--- a/.github/workflows/single-include.yml
+++ b/.github/workflows/single-include.yml
@@ -6,7 +6,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@master
+ - uses: actions/checkout@v2
- name: update single include
run: |
@@ -17,10 +17,6 @@ jobs:
working-directory: ${{runner.workspace}}/inja/single_include/
shell: bash
run: |
- pwd
- ls inja
- cat inja/inja.hpp
- cat inja/inja_old.hpp
diff inja/inja.hpp inja/inja_old.hpp >/dev/null
- uses: actions/upload-artifact@v1
diff --git a/.gitignore b/.gitignore
index 060ca13..88d9a4d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,8 +36,11 @@ dist
.coveralls.yml
.vscode
+.vs
doc/html
doc/latex
+__pycache__
+
examples
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 87691d4..772c9e0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.5)
-project(inja LANGUAGES CXX VERSION 3.1.0)
+project(inja LANGUAGES CXX VERSION 3.3.0)
option(INJA_USE_EMBEDDED_JSON "Use the shipped json header if not available on the system" ON)
@@ -12,30 +12,28 @@ option(INJA_BUILD_TESTS "Build unit tests when BUILD_TESTING is enabled." ON)
option(BUILD_BENCHMARK "Build benchmark" ON)
option(COVERALLS "Generate coveralls data" OFF)
-set(INJA_INSTALL_INCLUDE_DIR "include")
+set(INJA_INSTALL_INCLUDE_DIR "include")
+set(INJA_PACKAGE_USE_EMBEDDED_JSON OFF)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake)
-if(CMAKE_COMPILER_IS_GNUCC)
- add_compile_options(-Wall)
-endif()
+# For using the correct __cplusplus macro
if(MSVC)
- add_compile_options(/W4 /permissive- /utf-8 /Zc:__cplusplus)
+ add_compile_options(/utf-8 /Zc:__cplusplus)
endif()
add_library(inja INTERFACE)
add_library(pantor::inja ALIAS inja)
+
target_include_directories(inja INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${INJA_INSTALL_INCLUDE_DIR}>
)
+target_compile_features(inja INTERFACE cxx_std_17)
-target_compile_features(inja INTERFACE cxx_std_11)
-
-set(INJA_PACKAGE_USE_EMBEDDED_JSON OFF)
if(INJA_USE_EMBEDDED_JSON)
find_package(nlohmann_json QUIET)
@@ -49,7 +47,7 @@ if(INJA_USE_EMBEDDED_JSON)
$<INSTALL_INTERFACE:${INJA_INSTALL_INCLUDE_DIR}/inja/json/include>
)
- target_compile_features(nlohmann_json INTERFACE cxx_std_11)
+ target_compile_features(nlohmann_json INTERFACE cxx_std_17)
install(TARGETS nlohmann_json EXPORT injaTargets)
@@ -67,10 +65,11 @@ else()
endif()
endif()
+
target_link_libraries(inja INTERFACE ${INJA_SELECTED_JSON_LIBRARY})
+
execute_process(COMMAND scripts/update_single_include.sh WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
-# CMake: add_custom_command
if(BUILD_TESTING AND INJA_BUILD_TESTS)
@@ -78,12 +77,17 @@ if(BUILD_TESTING AND INJA_BUILD_TESTS)
add_executable(inja_test test/test.cpp)
target_link_libraries(inja_test PRIVATE inja)
-
add_test(inja_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/inja_test)
+ if(MSVC)
+ target_compile_options(inja_test PRIVATE /W4 /permissive-)
+ else()
+ target_compile_options(inja_test PRIVATE -Wall -Wextra -pedantic)
+ endif()
+
add_library(single_inja INTERFACE)
- target_compile_features(single_inja INTERFACE cxx_std_11)
+ target_compile_features(single_inja INTERFACE cxx_std_17)
target_include_directories(single_inja INTERFACE single_include include third_party/include)
add_executable(single_inja_test test/test.cpp)
@@ -96,6 +100,12 @@ endif()
if(BUILD_BENCHMARK)
add_executable(inja_benchmark test/benchmark.cpp)
target_link_libraries(inja_benchmark PRIVATE inja)
+
+ if(MSVC)
+ target_compile_options(inja_benchmark PRIVATE /W4 /permissive-)
+ else()
+ target_compile_options(inja_benchmark PRIVATE -Wall -Wextra -pedantic)
+ endif()
endif()
diff --git a/LICENSE b/LICENSE
index c6a3b44..9e06bea 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2018 lbersch
+Copyright (c) 2018-2021 Berscheid
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -19,32 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-
-
----
-
-
-Copyright (c) 2009-2018 FIRST
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * 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.
- * Neither the name of the FIRST 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 FIRST AND CONTRIBUTORS``AS IS'' AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST 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. \ No newline at end of file
diff --git a/README.md b/README.md
index 4c55556..aada14f 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,6 @@ Inja is a headers only library, which can be downloaded from the [releases](http
// Just for convenience
using namespace inja;
-using json = nlohmann::json;
```
If you are using the [Meson Build System](http://mesonbuild.com), then you can wrap this repository as a subproject.
@@ -184,20 +183,30 @@ env.render("Content: {% include \"content\" %}", data); // "Content: Hello Peter
// Other template files are included relative from the current file location
render("{% include \"footer.html\" %}", data);
+```
+If a corresponding template could not be found in the file system, the *include callback* is called:
+```.cpp
+// The callback takes the current path and the wanted include name and returns a template
+env.set_include_callback([&env](const std::string& path, const std::string& template_name) {
+ return env.parse("Hello {{ neighbour }} from " + template_name);
+});
// You can disable to search for templates in the file system via
env.set_search_included_templates_in_files(false);
```
-Inja will throw an `inja::RenderError` if an included file is not found. To disable this error, you can call `env.set_throw_at_missing_includes(false)`.
+Inja will throw an `inja::RenderError` if an included file is not found and no callback is specified. To disable this error, you can call `env.set_throw_at_missing_includes(false)`.
#### Assignments
Variables can also be defined within the template using the set statment.
```.cpp
render("{% set new_hour=23 %}{{ new_hour }}pm", data); // "23pm"
+render("{% set time.start=18 %}{{ time.start }}pm", data); // using json pointers
```
+Assignments only set the value within the rendering context; they do not modify the json object passed into the `render` call.
+
### Functions
A few functions are implemented within the inja template syntax. They can be called with
@@ -221,6 +230,10 @@ render("{{ last(guests) }} was last.", data); // "Patir was last."
render("{{ sort([3,2,1]) }}", data); // "[1,2,3]"
render("{{ sort(guests) }}", data); // "[\"Jeff\", \"Patrick\", \"Tom\"]"
+// Join a list with a separator
+render("{{ join([1,2,3], \" + \") }}", data); // "1 + 2 + 3"
+render("{{ join(guests, \", \") }}", data); // "Jeff, Patrick, Tom"
+
// Round numbers to a given precision
render("{{ round(3.1415, 0) }}", data); // 3
render("{{ round(3.1415, 3) }}", data); // 3.142
@@ -242,6 +255,9 @@ render("{{ float(\"1.8\") > 2 }}", data); // false
render("Hello {{ default(neighbour, \"my friend\") }}!", data); // "Hello Peter!"
render("Hello {{ default(colleague, \"my friend\") }}!", data); // "Hello my friend!"
+// Access an objects value dynamically
+render("{{ at(time, \"start\") }} to {{ time.end }}", data); // "16 to 22"
+
// Check if a key exists in an object
render("{{ exists(\"guests\") }}", data); // "true"
render("{{ exists(\"city\") }}", data); // "false"
@@ -254,25 +270,6 @@ render("{{ isArray(guests) }}", data); // "true"
// Implemented type checks: isArray, isBoolean, isFloat, isInteger, isNumber, isObject, isString,
```
-### Whitespace Control
-
-In the default configuration, no whitespace is removed while rendering the file. To support a more readable template style, you can configure the environment to control whitespaces before and after a statement automatically. While enabling `set_trim_blocks` removes the first newline after a statement, `set_lstrip_blocks` strips tabs and spaces from the beginning of a line to the start of a block.
-
-```.cpp
-Environment env;
-env.set_trim_blocks(true);
-env.set_lstrip_blocks(true);
-```
-
-With both `trim_blocks` and `lstrip_blocks` enabled, you can put statements on their own lines. Furthermore, you can also strip whitespaces for both statements and expressions by hand. If you add a minus sign (`-`) to the start or end, the whitespaces before or after that block will be removed:
-
-```.cpp
-render("Hello {{- name -}} !", data); // "Hello Inja!"
-render("{% if neighbour in guests -%} I was there{% endif -%} !", data); // Renders without any whitespaces
-```
-
-Stripping behind a statement or expression also removes any newlines.
-
### Callbacks
You can create your own and more complex functions with callbacks. These are implemented with `std::function`, so you can for example use C++ lambdas. Inja `Arguments` are a vector of json pointers.
@@ -316,6 +313,61 @@ env.add_void_callback("log", 1, [greet](Arguments args) {
env.render("{{ log(neighbour) }}", data); // Prints nothing to result, only to cout...
```
+### Template Inheritance
+
+Template inheritance allows you to build a base *skeleton* template that contains all the common elements and defines blocks that child templates can override. Lets show an example: The base template
+```.html
+<!DOCTYPE html>
+<html>
+<head>
+ {% block head %}
+ <link rel="stylesheet" href="style.css" />
+ <title>{% block title %}{% endblock %} - My Webpage</title>
+ {% endblock %}
+</head>
+<body>
+ <div id="content">{% block content %}{% endblock %}</div>
+</body>
+</html>
+```
+contains three `blocks` that child templates can fill in. The child template
+```.html
+{% extends "base.html" %}
+{% block title %}Index{% endblock %}
+{% block head %}
+ {{ super() }}
+ <style type="text/css">
+ .important { color: #336699; }
+ </style>
+{% endblock %}
+{% block content %}
+ <h1>Index</h1>
+ <p class="important">
+ Welcome to my blog!
+ </p>
+{% endblock %}
+```
+calls a parent template with the `extends` keyword; it should be the first element in the template. It is possible to render the contents of the parent block by calling `super()`. In the case of multiple levels of `{% extends %}`, super references may be called with an argument (e.g. `super(2)`) to skip levels in the inheritance tree.
+
+### Whitespace Control
+
+In the default configuration, no whitespace is removed while rendering the file. To support a more readable template style, you can configure the environment to control whitespaces before and after a statement automatically. While enabling `set_trim_blocks` removes the first newline after a statement, `set_lstrip_blocks` strips tabs and spaces from the beginning of a line to the start of a block.
+
+```.cpp
+Environment env;
+env.set_trim_blocks(true);
+env.set_lstrip_blocks(true);
+```
+
+With both `trim_blocks` and `lstrip_blocks` enabled, you can put statements on their own lines. Furthermore, you can also strip whitespaces for both statements and expressions by hand. If you add a minus sign (`-`) to the start or end, the whitespaces before or after that block will be removed:
+
+```.cpp
+render("Hello {{- name -}} !", data); // "Hello Inja!"
+render("{% if neighbour in guests -%} I was there{% endif -%} !", data); // Renders without any whitespaces
+```
+
+Stripping behind a statement or expression also removes any newlines.
+
### Comments
Comments can be written with the `{# ... #}` syntax.
@@ -330,10 +382,10 @@ Inja uses exceptions to handle ill-formed template input. However, exceptions ca
## Supported compilers
-Inja uses `string_view` from C++17, but includes the [polyfill](https://github.com/martinmoene/string-view-lite) from martinmoene. This way, the minimum version is C++11. Currently, the following compilers are tested:
+Inja uses the `string_view` feature of the C++17 STL. Currently, the following compilers are tested:
-- GCC 4.8 - 9 (and possibly later)
-- Clang 3.5 - 9 (and possibly later)
-- Microsoft Visual C++ 2016 - 2019 (and possibly later)
+- GCC 7 - 11 (and possibly later)
+- Clang 5 - 12 (and possibly later)
+- Microsoft Visual C++ 2017 15.0 - 2022 (and possibly later)
-The unit tests fail to compile with GCC 4.8 but should just work fine. A complete list of supported compiler / os versions can be found in the [CI definition](https://github.com/pantor/inja/blob/master/.github/workflows/ci.yml).
+A list of supported compiler / os versions can be found in the [CI definition](https://github.com/pantor/inja/blob/master/.github/workflows/ci.yml).
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 10bbe55..d00832e 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = "Inja"
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = 3.1.0
+PROJECT_NUMBER = 3.3.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/include/inja/config.hpp b/include/inja/config.hpp
index 3f284a4..0a8f9b7 100644
--- a/include/inja/config.hpp
+++ b/include/inja/config.hpp
@@ -1,12 +1,10 @@
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_CONFIG_HPP_
#define INCLUDE_INJA_CONFIG_HPP_
#include <functional>
#include <string>
-#include "string_view.hpp"
+#include "template.hpp"
namespace inja {
@@ -25,7 +23,9 @@ struct LexerConfig {
std::string expression_close {"}}"};
std::string expression_close_force_rstrip {"-}}"};
std::string comment_open {"{#"};
+ std::string comment_open_force_lstrip {"{#-"};
std::string comment_close {"#}"};
+ std::string comment_close_force_rstrip {"-#}"};
std::string open_chars {"#{"};
bool trim_blocks {false};
@@ -54,6 +54,9 @@ struct LexerConfig {
if (open_chars.find(comment_open[0]) == std::string::npos) {
open_chars += comment_open[0];
}
+ if (open_chars.find(comment_open_force_lstrip[0]) == std::string::npos) {
+ open_chars += comment_open_force_lstrip[0];
+ }
}
};
@@ -62,6 +65,8 @@ struct LexerConfig {
*/
struct ParserConfig {
bool search_included_templates_in_files {true};
+
+ std::function<Template(const std::string&, const std::string&)> include_callback;
};
/*!
diff --git a/include/inja/environment.hpp b/include/inja/environment.hpp
index ed99537..a652935 100644
--- a/include/inja/environment.hpp
+++ b/include/inja/environment.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
#define INCLUDE_INJA_ENVIRONMENT_HPP_
@@ -8,21 +6,17 @@
#include <memory>
#include <sstream>
#include <string>
-
-#include <nlohmann/json.hpp>
+#include <string_view>
#include "config.hpp"
#include "function_storage.hpp"
#include "parser.hpp"
#include "renderer.hpp"
-#include "string_view.hpp"
#include "template.hpp"
#include "utils.hpp"
namespace inja {
-using json = nlohmann::json;
-
/*!
* \brief Class for changing the configuration.
*/
@@ -38,15 +32,14 @@ class Environment {
TemplateStorage template_storage;
public:
- Environment() : Environment("") {}
+ Environment(): Environment("") {}
- explicit Environment(const std::string &global_path) : input_path(global_path), output_path(global_path) {}
+ explicit Environment(const std::string& global_path): input_path(global_path), output_path(global_path) {}
- Environment(const std::string &input_path, const std::string &output_path)
- : input_path(input_path), output_path(output_path) {}
+ Environment(const std::string& input_path, const std::string& output_path): input_path(input_path), output_path(output_path) {}
/// Sets the opener and closer for template statements
- void set_statement(const std::string &open, const std::string &close) {
+ void set_statement(const std::string& open, const std::string& close) {
lexer_config.statement_open = open;
lexer_config.statement_open_no_lstrip = open + "+";
lexer_config.statement_open_force_lstrip = open + "-";
@@ -56,13 +49,13 @@ public:
}
/// Sets the opener for template line statements
- void set_line_statement(const std::string &open) {
+ void set_line_statement(const std::string& open) {
lexer_config.line_statement = open;
lexer_config.update_open_chars();
}
/// Sets the opener and closer for template expressions
- void set_expression(const std::string &open, const std::string &close) {
+ void set_expression(const std::string& open, const std::string& close) {
lexer_config.expression_open = open;
lexer_config.expression_open_force_lstrip = open + "-";
lexer_config.expression_close = close;
@@ -71,9 +64,11 @@ public:
}
/// Sets the opener and closer for template comments
- void set_comment(const std::string &open, const std::string &close) {
+ void set_comment(const std::string& open, const std::string& close) {
lexer_config.comment_open = open;
+ lexer_config.comment_open_force_lstrip = open + "-";
lexer_config.comment_close = close;
+ lexer_config.comment_close_force_rstrip = "-" + close;
lexer_config.update_open_chars();
}
@@ -97,75 +92,79 @@ public:
render_config.throw_at_missing_includes = will_throw;
}
- Template parse(nonstd::string_view input) {
+ Template parse(std::string_view input) {
Parser parser(parser_config, lexer_config, template_storage, function_storage);
return parser.parse(input);
}
- Template parse_template(const std::string &filename) {
+ Template parse_template(const std::string& filename) {
Parser parser(parser_config, lexer_config, template_storage, function_storage);
auto result = Template(parser.load_file(input_path + static_cast<std::string>(filename)));
parser.parse_into_template(result, input_path + static_cast<std::string>(filename));
return result;
}
- Template parse_file(const std::string &filename) {
+ Template parse_file(const std::string& filename) {
return parse_template(filename);
}
- std::string render(nonstd::string_view input, const json &data) { return render(parse(input), data); }
+ std::string render(std::string_view input, const json& data) {
+ return render(parse(input), data);
+ }
- std::string render(const Template &tmpl, const json &data) {
+ std::string render(const Template& tmpl, const json& data) {
std::stringstream os;
render_to(os, tmpl, data);
return os.str();
}
- std::string render_file(const std::string &filename, const json &data) {
+ std::string render_file(const std::string& filename, const json& data) {
return render(parse_template(filename), data);
}
- std::string render_file_with_json_file(const std::string &filename, const std::string &filename_data) {
+ std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) {
const json data = load_json(filename_data);
return render_file(filename, data);
}
- void write(const std::string &filename, const json &data, const std::string &filename_out) {
+ void write(const std::string& filename, const json& data, const std::string& filename_out) {
std::ofstream file(output_path + filename_out);
file << render_file(filename, data);
file.close();
}
- void write(const Template &temp, const json &data, const std::string &filename_out) {
+ void write(const Template& temp, const json& data, const std::string& filename_out) {
std::ofstream file(output_path + filename_out);
file << render(temp, data);
file.close();
}
- void write_with_json_file(const std::string &filename, const std::string &filename_data,
- const std::string &filename_out) {
+ void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
const json data = load_json(filename_data);
write(filename, data, filename_out);
}
- void write_with_json_file(const Template &temp, const std::string &filename_data, const std::string &filename_out) {
+ void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
const json data = load_json(filename_data);
write(temp, data, filename_out);
}
- std::ostream &render_to(std::ostream &os, const Template &tmpl, const json &data) {
+ std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data);
return os;
}
- std::string load_file(const std::string &filename) {
+ std::string load_file(const std::string& filename) {
Parser parser(parser_config, lexer_config, template_storage, function_storage);
return parser.load_file(input_path + filename);
}
- json load_json(const std::string &filename) {
+ json load_json(const std::string& filename) {
std::ifstream file;
- open_file_or_throw(input_path + filename, file);
+ file.open(input_path + filename);
+ if (file.fail()) {
+ INJA_THROW(FileError("failed accessing file at '" + input_path + filename + "'"));
+ }
json j;
file >> j;
return j;
@@ -174,51 +173,61 @@ public:
/*!
@brief Adds a variadic callback
*/
- void add_callback(const std::string &name, const CallbackFunction &callback) {
+ void add_callback(const std::string& name, const CallbackFunction& callback) {
add_callback(name, -1, callback);
}
/*!
@brief Adds a variadic void callback
*/
- void add_void_callback(const std::string &name, const VoidCallbackFunction &callback) {
+ void add_void_callback(const std::string& name, const VoidCallbackFunction& callback) {
add_void_callback(name, -1, callback);
}
/*!
@brief Adds a callback with given number or arguments
*/
- void add_callback(const std::string &name, int num_args, const CallbackFunction &callback) {
+ void add_callback(const std::string& name, int num_args, const CallbackFunction& callback) {
function_storage.add_callback(name, num_args, callback);
}
/*!
@brief Adds a void callback with given number or arguments
*/
- void add_void_callback(const std::string &name, int num_args, const VoidCallbackFunction &callback) {
- function_storage.add_callback(name, num_args, [callback](Arguments& args) { callback(args); return json(); });
+ void add_void_callback(const std::string& name, int num_args, const VoidCallbackFunction& callback) {
+ function_storage.add_callback(name, num_args, [callback](Arguments& args) {
+ callback(args);
+ return json();
+ });
}
/** Includes a template with a given name into the environment.
* Then, a template can be rendered in another template using the
* include "<name>" syntax.
*/
- void include_template(const std::string &name, const Template &tmpl) {
+ void include_template(const std::string& name, const Template& tmpl) {
template_storage[name] = tmpl;
}
+
+ /*!
+ @brief Sets a function that is called when an included file is not found
+ */
+ void set_include_callback(const std::function<Template(const std::string&, const std::string&)>& callback) {
+ parser_config.include_callback = callback;
+ }
};
/*!
@brief render with default settings to a string
*/
-inline std::string render(nonstd::string_view input, const json &data) {
+inline std::string render(std::string_view input, const json& data) {
return Environment().render(input, data);
}
/*!
@brief render with default settings to the given output stream
*/
-inline void render_to(std::ostream &os, nonstd::string_view input, const json &data) {
+inline void render_to(std::ostream& os, std::string_view input, const json& data) {
Environment env;
env.render_to(os, env.parse(input), data);
}
diff --git a/include/inja/exceptions.hpp b/include/inja/exceptions.hpp
index 2784da8..f0dc8aa 100644
--- a/include/inja/exceptions.hpp
+++ b/include/inja/exceptions.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
#define INCLUDE_INJA_EXCEPTIONS_HPP_
@@ -19,30 +17,29 @@ struct InjaError : public std::runtime_error {
const SourceLocation location;
- explicit InjaError(const std::string &type, const std::string &message)
+ explicit InjaError(const std::string& type, const std::string& message)
: std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {}
- explicit InjaError(const std::string &type, const std::string &message, SourceLocation location)
- : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" +
- std::to_string(location.column) + ") " + message),
+ explicit InjaError(const std::string& type, const std::string& message, SourceLocation location)
+ : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" + std::to_string(location.column) + ") " + message),
type(type), message(message), location(location) {}
};
struct ParserError : public InjaError {
- explicit ParserError(const std::string &message, SourceLocation location) : InjaError("parser_error", message, location) {}
+ explicit ParserError(const std::string& message, SourceLocation location): InjaError("parser_error", message, location) {}
};
struct RenderError : public InjaError {
- explicit RenderError(const std::string &message, SourceLocation location) : InjaError("render_error", message, location) {}
+ explicit RenderError(const std::string& message, SourceLocation location): InjaError("render_error", message, location) {}
};
struct FileError : public InjaError {
- explicit FileError(const std::string &message) : InjaError("file_error", message) {}
- explicit FileError(const std::string &message, SourceLocation location) : InjaError("file_error", message, location) {}
+ explicit FileError(const std::string& message): InjaError("file_error", message) {}
+ explicit FileError(const std::string& message, SourceLocation location): InjaError("file_error", message, location) {}
};
-struct JsonError : public InjaError {
- explicit JsonError(const std::string &message, SourceLocation location) : InjaError("json_error", message, location) {}
+struct DataError : public InjaError {
+ explicit DataError(const std::string& message, SourceLocation location): InjaError("data_error", message, location) {}
};
} // namespace inja
diff --git a/include/inja/function_storage.hpp b/include/inja/function_storage.hpp
index b0091bd..891e45f 100644
--- a/include/inja/function_storage.hpp
+++ b/include/inja/function_storage.hpp
@@ -1,19 +1,14 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
+#include <string_view>
#include <vector>
-#include "string_view.hpp"
-
namespace inja {
-using json = nlohmann::json;
-
-using Arguments = std::vector<const json *>;
-using CallbackFunction = std::function<json(Arguments &args)>;
-using VoidCallbackFunction = std::function<void(Arguments &args)>;
+using Arguments = std::vector<const json*>;
+using CallbackFunction = std::function<json(Arguments& args)>;
+using VoidCallbackFunction = std::function<void(Arguments& args)>;
/*!
* \brief Class for builtin functions and user-defined callbacks.
@@ -64,6 +59,8 @@ public:
Round,
Sort,
Upper,
+ Super,
+ Join,
Callback,
ParenLeft,
ParenRight,
@@ -71,7 +68,7 @@ public:
};
struct FunctionData {
- explicit FunctionData(const Operation &op, const CallbackFunction &cb = CallbackFunction{}) : operation(op), callback(cb) {}
+ explicit FunctionData(const Operation& op, const CallbackFunction& cb = CallbackFunction {}): operation(op), callback(cb) {}
const Operation operation;
const CallbackFunction callback;
};
@@ -80,49 +77,52 @@ private:
const int VARIADIC {-1};
std::map<std::pair<std::string, int>, FunctionData> function_storage = {
- {std::make_pair("at", 2), FunctionData { Operation::At }},
- {std::make_pair("default", 2), FunctionData { Operation::Default }},
- {std::make_pair("divisibleBy", 2), FunctionData { Operation::DivisibleBy }},
- {std::make_pair("even", 1), FunctionData { Operation::Even }},
- {std::make_pair("exists", 1), FunctionData { Operation::Exists }},
- {std::make_pair("existsIn", 2), FunctionData { Operation::ExistsInObject }},
- {std::make_pair("first", 1), FunctionData { Operation::First }},
- {std::make_pair("float", 1), FunctionData { Operation::Float }},
- {std::make_pair("int", 1), FunctionData { Operation::Int }},
- {std::make_pair("isArray", 1), FunctionData { Operation::IsArray }},
- {std::make_pair("isBoolean", 1), FunctionData { Operation::IsBoolean }},
- {std::make_pair("isFloat", 1), FunctionData { Operation::IsFloat }},
- {std::make_pair("isInteger", 1), FunctionData { Operation::IsInteger }},
- {std::make_pair("isNumber", 1), FunctionData { Operation::IsNumber }},
- {std::make_pair("isObject", 1), FunctionData { Operation::IsObject }},
- {std::make_pair("isString", 1), FunctionData { Operation::IsString }},
- {std::make_pair("last", 1), FunctionData { Operation::Last }},
- {std::make_pair("length", 1), FunctionData { Operation::Length }},
- {std::make_pair("lower", 1), FunctionData { Operation::Lower }},
- {std::make_pair("max", 1), FunctionData { Operation::Max }},
- {std::make_pair("min", 1), FunctionData { Operation::Min }},
- {std::make_pair("odd", 1), FunctionData { Operation::Odd }},
- {std::make_pair("range", 1), FunctionData { Operation::Range }},
- {std::make_pair("round", 2), FunctionData { Operation::Round }},
- {std::make_pair("sort", 1), FunctionData { Operation::Sort }},
- {std::make_pair("upper", 1), FunctionData { Operation::Upper }},
+ {std::make_pair("at", 2), FunctionData {Operation::At}},
+ {std::make_pair("default", 2), FunctionData {Operation::Default}},
+ {std::make_pair("divisibleBy", 2), FunctionData {Operation::DivisibleBy}},
+ {std::make_pair("even", 1), FunctionData {Operation::Even}},
+ {std::make_pair("exists", 1), FunctionData {Operation::Exists}},
+ {std::make_pair("existsIn", 2), FunctionData {Operation::ExistsInObject}},
+ {std::make_pair("first", 1), FunctionData {Operation::First}},
+ {std::make_pair("float", 1), FunctionData {Operation::Float}},
+ {std::make_pair("int", 1), FunctionData {Operation::Int}},
+ {std::make_pair("isArray", 1), FunctionData {Operation::IsArray}},
+ {std::make_pair("isBoolean", 1), FunctionData {Operation::IsBoolean}},
+ {std::make_pair("isFloat", 1), FunctionData {Operation::IsFloat}},
+ {std::make_pair("isInteger", 1), FunctionData {Operation::IsInteger}},
+ {std::make_pair("isNumber", 1), FunctionData {Operation::IsNumber}},
+ {std::make_pair("isObject", 1), FunctionData {Operation::IsObject}},
+ {std::make_pair("isString", 1), FunctionData {Operation::IsString}},
+ {std::make_pair("last", 1), FunctionData {Operation::Last}},
+ {std::make_pair("length", 1), FunctionData {Operation::Length}},
+ {std::make_pair("lower", 1), FunctionData {Operation::Lower}},
+ {std::make_pair("max", 1), FunctionData {Operation::Max}},
+ {std::make_pair("min", 1), FunctionData {Operation::Min}},
+ {std::make_pair("odd", 1), FunctionData {Operation::Odd}},
+ {std::make_pair("range", 1), FunctionData {Operation::Range}},
+ {std::make_pair("round", 2), FunctionData {Operation::Round}},
+ {std::make_pair("sort", 1), FunctionData {Operation::Sort}},
+ {std::make_pair("upper", 1), FunctionData {Operation::Upper}},
+ {std::make_pair("super", 0), FunctionData {Operation::Super}},
+ {std::make_pair("super", 1), FunctionData {Operation::Super}},
+ {std::make_pair("join", 2), FunctionData {Operation::Join}},
};
public:
- void add_builtin(nonstd::string_view name, int num_args, Operation op) {
- function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData { op });
+ void add_builtin(std::string_view name, int num_args, Operation op) {
+ function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {op});
}
- void add_callback(nonstd::string_view name, int num_args, const CallbackFunction &callback) {
- function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData { Operation::Callback, callback });
+ void add_callback(std::string_view name, int num_args, const CallbackFunction& callback) {
+ function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {Operation::Callback, callback});
}
- FunctionData find_function(nonstd::string_view name, int num_args) const {
+ FunctionData find_function(std::string_view name, int num_args) const {
auto it = function_storage.find(std::make_pair(static_cast<std::string>(name), num_args));
if (it != function_storage.end()) {
return it->second;
- // Find variadic function
+ // Find variadic function
} else if (num_args > 0) {
it = function_storage.find(std::make_pair(static_cast<std::string>(name), VARIADIC));
if (it != function_storage.end()) {
@@ -130,7 +130,7 @@ public:
}
}
- return FunctionData { Operation::None };
+ return FunctionData {Operation::None};
}
};
diff --git a/include/inja/inja.hpp b/include/inja/inja.hpp
index 86b8a0a..381da4b 100644
--- a/include/inja/inja.hpp
+++ b/include/inja/inja.hpp
@@ -1,22 +1,60 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
+/*
+ ___ _ Version 3.3
+ |_ _|_ __ (_) __ _ https://github.com/pantor/inja
+ | || '_ \ | |/ _` | Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+ | || | | || | (_| |
+ |___|_| |_|/ |\__,_| Copyright (c) 2018-2021 Lars Berscheid
+ |__/
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
#ifndef INCLUDE_INJA_INJA_HPP_
#define INCLUDE_INJA_INJA_HPP_
#include <nlohmann/json.hpp>
+namespace inja {
+#ifndef INJA_DATA_TYPE
+using json = nlohmann::json;
+#else
+using json = INJA_DATA_TYPE;
+#endif
+} // namespace inja
+
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION)
- #define INJA_THROW(exception) throw exception
+#ifndef INJA_THROW
+#define INJA_THROW(exception) throw exception
+#endif
#else
- #include <cstdlib>
- #define INJA_THROW(exception) std::abort()
+#include <cstdlib>
+#ifndef INJA_THROW
+#define INJA_THROW(exception) \
+ std::abort(); \
+ std::ignore = exception
+#endif
+#ifndef INJA_NOEXCEPTION
+#define INJA_NOEXCEPTION
+#endif
#endif
#include "environment.hpp"
#include "exceptions.hpp"
#include "parser.hpp"
#include "renderer.hpp"
-#include "string_view.hpp"
#include "template.hpp"
#endif // INCLUDE_INJA_INJA_HPP_
diff --git a/include/inja/lexer.hpp b/include/inja/lexer.hpp
index e31c3d6..c777600 100644
--- a/include/inja/lexer.hpp
+++ b/include/inja/lexer.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_LEXER_HPP_
#define INCLUDE_INJA_LEXER_HPP_
@@ -28,6 +26,7 @@ class Lexer {
StatementStartForceLstrip,
StatementBody,
CommentStart,
+ CommentStartForceLstrip,
CommentBody,
};
@@ -36,22 +35,21 @@ class Lexer {
Number,
};
- const LexerConfig &config;
+ const LexerConfig& config;
State state;
MinusState minus_state;
- nonstd::string_view m_in;
+ std::string_view m_in;
size_t tok_start;
size_t pos;
-
- Token scan_body(nonstd::string_view close, Token::Kind closeKind, nonstd::string_view close_trim = nonstd::string_view(), bool trim = false) {
+ Token scan_body(std::string_view close, Token::Kind closeKind, std::string_view close_trim = std::string_view(), bool trim = false) {
again:
// skip whitespace (except for \n as it might be a close)
if (tok_start >= m_in.size()) {
return make_token(Token::Kind::Eof);
}
- char ch = m_in[tok_start];
+ const char ch = m_in[tok_start];
if (ch == ' ' || ch == '\t' || ch == '\r') {
tok_start += 1;
goto again;
@@ -61,7 +59,7 @@ class Lexer {
if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) {
state = State::Text;
pos = tok_start + close_trim.size();
- Token tok = make_token(closeKind);
+ const Token tok = make_token(closeKind);
skip_whitespaces_and_newlines();
return tok;
}
@@ -69,7 +67,7 @@ class Lexer {
if (inja::string_view::starts_with(m_in.substr(tok_start), close)) {
state = State::Text;
pos = tok_start + close.size();
- Token tok = make_token(closeKind);
+ const Token tok = make_token(closeKind);
if (trim) {
skip_whitespaces_and_first_newline();
}
@@ -88,7 +86,7 @@ class Lexer {
return scan_id();
}
- MinusState current_minus_state = minus_state;
+ const MinusState current_minus_state = minus_state;
if (minus_state == MinusState::Operator) {
minus_state = MinusState::Number;
}
@@ -183,7 +181,7 @@ class Lexer {
if (pos >= m_in.size()) {
break;
}
- char ch = m_in[pos];
+ const char ch = m_in[pos];
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
break;
}
@@ -197,7 +195,7 @@ class Lexer {
if (pos >= m_in.size()) {
break;
}
- char ch = m_in[pos];
+ const char ch = m_in[pos];
// be very permissive in lexer (we'll catch errors when conversion happens)
if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-') {
break;
@@ -213,7 +211,7 @@ class Lexer {
if (pos >= m_in.size()) {
break;
}
- char ch = m_in[pos++];
+ const char ch = m_in[pos++];
if (ch == '\\') {
escape = true;
} else if (!escape && ch == m_in[tok_start]) {
@@ -225,7 +223,9 @@ class Lexer {
return make_token(Token::Kind::String);
}
- Token make_token(Token::Kind kind) const { return Token(kind, string_view::slice(m_in, tok_start, pos)); }
+ Token make_token(Token::Kind kind) const {
+ return Token(kind, string_view::slice(m_in, tok_start, pos));
+ }
void skip_whitespaces_and_newlines() {
if (pos < m_in.size()) {
@@ -243,7 +243,7 @@ class Lexer {
}
if (pos < m_in.size()) {
- char ch = m_in[pos];
+ const char ch = m_in[pos];
if (ch == '\n') {
pos += 1;
} else if (ch == '\r') {
@@ -255,10 +255,10 @@ class Lexer {
}
}
- static nonstd::string_view clear_final_line_if_whitespace(nonstd::string_view text) {
- nonstd::string_view result = text;
+ static std::string_view clear_final_line_if_whitespace(std::string_view text) {
+ std::string_view result = text;
while (!result.empty()) {
- char ch = result.back();
+ const char ch = result.back();
if (ch == ' ' || ch == '\t') {
result.remove_suffix(1);
} else if (ch == '\n' || ch == '\r') {
@@ -271,13 +271,13 @@ class Lexer {
}
public:
- explicit Lexer(const LexerConfig &config) : config(config), state(State::Text), minus_state(MinusState::Number) {}
+ explicit Lexer(const LexerConfig& config): config(config), state(State::Text), minus_state(MinusState::Number) {}
SourceLocation current_position() const {
return get_source_location(m_in, tok_start);
}
- void start(nonstd::string_view input) {
+ void start(std::string_view input) {
m_in = input;
tok_start = 0;
pos = 0;
@@ -302,8 +302,8 @@ public:
default:
case State::Text: {
// fast-scan to first open character
- size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
- if (open_start == nonstd::string_view::npos) {
+ const size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
+ if (open_start == std::string_view::npos) {
// didn't find open, return remaining text as text token
pos = m_in.size();
return make_token(Token::Kind::Text);
@@ -311,7 +311,7 @@ public:
pos += open_start;
// try to match one of the opening sequences, and get the close
- nonstd::string_view open_str = m_in.substr(pos);
+ std::string_view open_str = m_in.substr(pos);
bool must_lstrip = false;
if (inja::string_view::starts_with(open_str, config.expression_open)) {
if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) {
@@ -323,7 +323,7 @@ public:
} else if (inja::string_view::starts_with(open_str, config.statement_open)) {
if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) {
state = State::StatementStartNoLstrip;
- } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip )) {
+ } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip)) {
state = State::StatementStartForceLstrip;
must_lstrip = true;
} else {
@@ -331,8 +331,13 @@ public:
must_lstrip = config.lstrip_blocks;
}
} else if (inja::string_view::starts_with(open_str, config.comment_open)) {
- state = State::CommentStart;
- must_lstrip = config.lstrip_blocks;
+ if (inja::string_view::starts_with(open_str, config.comment_open_force_lstrip)) {
+ state = State::CommentStartForceLstrip;
+ must_lstrip = true;
+ } else {
+ state = State::CommentStart;
+ must_lstrip = config.lstrip_blocks;
+ }
} else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) {
state = State::LineStart;
} else {
@@ -340,7 +345,7 @@ public:
goto again;
}
- nonstd::string_view text = string_view::slice(m_in, tok_start, pos);
+ std::string_view text = string_view::slice(m_in, tok_start, pos);
if (must_lstrip) {
text = clear_final_line_if_whitespace(text);
}
@@ -385,6 +390,11 @@ public:
pos += config.comment_open.size();
return make_token(Token::Kind::CommentOpen);
}
+ case State::CommentStartForceLstrip: {
+ state = State::CommentBody;
+ pos += config.comment_open_force_lstrip.size();
+ return make_token(Token::Kind::CommentOpen);
+ }
case State::ExpressionBody:
return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip);
case State::LineBody:
@@ -393,16 +403,21 @@ public:
return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks);
case State::CommentBody: {
// fast-scan to comment close
- size_t end = m_in.substr(pos).find(config.comment_close);
- if (end == nonstd::string_view::npos) {
+ const size_t end = m_in.substr(pos).find(config.comment_close);
+ if (end == std::string_view::npos) {
pos = m_in.size();
return make_token(Token::Kind::Eof);
}
+
+ // Check for trim pattern
+ const bool must_rstrip = inja::string_view::starts_with(m_in.substr(pos + end - 1), config.comment_close_force_rstrip);
+
// return the entire comment in the close token
state = State::Text;
pos += end + config.comment_close.size();
Token tok = make_token(Token::Kind::CommentClose);
- if (config.trim_blocks) {
+
+ if (must_rstrip || config.trim_blocks) {
skip_whitespaces_and_first_newline();
}
return tok;
@@ -410,7 +425,7 @@ public:
}
}
- const LexerConfig &get_config() const {
+ const LexerConfig& get_config() const {
return config;
}
};
diff --git a/include/inja/node.hpp b/include/inja/node.hpp
index 84a8b8a..1946805 100644
--- a/include/inja/node.hpp
+++ b/include/inja/node.hpp
@@ -1,16 +1,12 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_NODE_HPP_
#define INCLUDE_INJA_NODE_HPP_
#include <string>
+#include <string_view>
#include <utility>
-#include <nlohmann/json.hpp>
-
#include "function_storage.hpp"
-#include "string_view.hpp"
-
+#include "utils.hpp"
namespace inja {
@@ -19,7 +15,7 @@ class BlockNode;
class TextNode;
class ExpressionNode;
class LiteralNode;
-class JsonNode;
+class DataNode;
class FunctionNode;
class ExpressionListNode;
class StatementNode;
@@ -28,16 +24,19 @@ class ForArrayStatementNode;
class ForObjectStatementNode;
class IfStatementNode;
class IncludeStatementNode;
+class ExtendsStatementNode;
+class BlockStatementNode;
class SetStatementNode;
-
class NodeVisitor {
public:
+ virtual ~NodeVisitor() = default;
+
virtual void visit(const BlockNode& node) = 0;
virtual void visit(const TextNode& node) = 0;
virtual void visit(const ExpressionNode& node) = 0;
virtual void visit(const LiteralNode& node) = 0;
- virtual void visit(const JsonNode& node) = 0;
+ virtual void visit(const DataNode& node) = 0;
virtual void visit(const FunctionNode& node) = 0;
virtual void visit(const ExpressionListNode& node) = 0;
virtual void visit(const StatementNode& node) = 0;
@@ -46,6 +45,8 @@ public:
virtual void visit(const ForObjectStatementNode& node) = 0;
virtual void visit(const IfStatementNode& node) = 0;
virtual void visit(const IncludeStatementNode& node) = 0;
+ virtual void visit(const ExtendsStatementNode& node) = 0;
+ virtual void visit(const BlockStatementNode& node) = 0;
virtual void visit(const SetStatementNode& node) = 0;
};
@@ -58,16 +59,15 @@ public:
size_t pos;
- AstNode(size_t pos) : pos(pos) { }
- virtual ~AstNode() { };
+ AstNode(size_t pos): pos(pos) {}
+ virtual ~AstNode() {}
};
-
class BlockNode : public AstNode {
public:
std::vector<std::shared_ptr<AstNode>> nodes;
- explicit BlockNode() : AstNode(0) {}
+ explicit BlockNode(): AstNode(0) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -78,7 +78,7 @@ class TextNode : public AstNode {
public:
const size_t length;
- explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) { }
+ explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -87,7 +87,7 @@ public:
class ExpressionNode : public AstNode {
public:
- explicit ExpressionNode(size_t pos) : AstNode(pos) {}
+ explicit ExpressionNode(size_t pos): AstNode(pos) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -96,24 +96,24 @@ public:
class LiteralNode : public ExpressionNode {
public:
- const nlohmann::json value;
+ const json value;
- explicit LiteralNode(const nlohmann::json& value, size_t pos) : ExpressionNode(pos), value(value) { }
+ explicit LiteralNode(std::string_view data_text, size_t pos): ExpressionNode(pos), value(json::parse(data_text)) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
}
};
-class JsonNode : public ExpressionNode {
+class DataNode : public ExpressionNode {
public:
const std::string name;
const json::json_pointer ptr;
- static std::string convert_dot_to_json_ptr(nonstd::string_view ptr_name) {
+ static std::string convert_dot_to_ptr(std::string_view ptr_name) {
std::string result;
do {
- nonstd::string_view part;
+ std::string_view part;
std::tie(part, ptr_name) = string_view::split(ptr_name, '.');
result.push_back('/');
result.append(part.begin(), part.end());
@@ -121,7 +121,7 @@ public:
return result;
}
- explicit JsonNode(nonstd::string_view ptr_name, size_t pos) : ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_json_ptr(ptr_name))) { }
+ explicit DataNode(std::string_view ptr_name, size_t pos): ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -144,83 +144,102 @@ public:
std::string name;
int number_args; // Should also be negative -> -1 for unknown number
+ std::vector<std::shared_ptr<ExpressionNode>> arguments;
CallbackFunction callback;
- explicit FunctionNode(nonstd::string_view name, size_t pos) : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) { }
- explicit FunctionNode(Op operation, size_t pos) : ExpressionNode(pos), operation(operation), number_args(1) {
+ explicit FunctionNode(std::string_view name, size_t pos)
+ : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) {}
+ explicit FunctionNode(Op operation, size_t pos): ExpressionNode(pos), operation(operation), number_args(1) {
switch (operation) {
- case Op::Not: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::And: {
- precedence = 1;
- associativity = Associativity::Left;
- } break;
- case Op::Or: {
- precedence = 1;
- associativity = Associativity::Left;
- } break;
- case Op::In: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Equal: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::NotEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Greater: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::GreaterEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Less: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::LessEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Add: {
- precedence = 3;
- associativity = Associativity::Left;
- } break;
- case Op::Subtract: {
- precedence = 3;
- associativity = Associativity::Left;
- } break;
- case Op::Multiplication: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::Division: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::Power: {
- precedence = 5;
- associativity = Associativity::Right;
- } break;
- case Op::Modulo: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::AtId: {
- precedence = 8;
- associativity = Associativity::Left;
- } break;
- default: {
- precedence = 1;
- associativity = Associativity::Left;
- }
+ case Op::Not: {
+ number_args = 1;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::And: {
+ number_args = 2;
+ precedence = 1;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Or: {
+ number_args = 2;
+ precedence = 1;
+ associativity = Associativity::Left;
+ } break;
+ case Op::In: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Equal: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::NotEqual: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Greater: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::GreaterEqual: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Less: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::LessEqual: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Add: {
+ number_args = 2;
+ precedence = 3;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Subtract: {
+ number_args = 2;
+ precedence = 3;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Multiplication: {
+ number_args = 2;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Division: {
+ number_args = 2;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Power: {
+ number_args = 2;
+ precedence = 5;
+ associativity = Associativity::Right;
+ } break;
+ case Op::Modulo: {
+ number_args = 2;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::AtId: {
+ number_args = 2;
+ precedence = 8;
+ associativity = Associativity::Left;
+ } break;
+ default: {
+ precedence = 1;
+ associativity = Associativity::Left;
+ }
}
}
@@ -231,10 +250,10 @@ public:
class ExpressionListNode : public AstNode {
public:
- std::vector<std::shared_ptr<ExpressionNode>> rpn_output;
+ std::shared_ptr<ExpressionNode> root;
- explicit ExpressionListNode() : AstNode(0) { }
- explicit ExpressionListNode(size_t pos) : AstNode(pos) { }
+ explicit ExpressionListNode(): AstNode(0) {}
+ explicit ExpressionListNode(size_t pos): AstNode(pos) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -243,7 +262,7 @@ public:
class StatementNode : public AstNode {
public:
- StatementNode(size_t pos) : AstNode(pos) { }
+ StatementNode(size_t pos): AstNode(pos) {}
virtual void accept(NodeVisitor& v) const = 0;
};
@@ -252,9 +271,9 @@ class ForStatementNode : public StatementNode {
public:
ExpressionListNode condition;
BlockNode body;
- BlockNode *const parent;
+ BlockNode* const parent;
- ForStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent) { }
+ ForStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent) {}
virtual void accept(NodeVisitor& v) const = 0;
};
@@ -263,7 +282,7 @@ class ForArrayStatementNode : public ForStatementNode {
public:
const std::string value;
- explicit ForArrayStatementNode(const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), value(value) { }
+ explicit ForArrayStatementNode(const std::string& value, BlockNode* const parent, size_t pos): ForStatementNode(parent, pos), value(value) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -275,7 +294,8 @@ public:
const std::string key;
const std::string value;
- explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), key(key), value(value) { }
+ explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode* const parent, size_t pos)
+ : ForStatementNode(parent, pos), key(key), value(value) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -287,13 +307,13 @@ public:
ExpressionListNode condition;
BlockNode true_statement;
BlockNode false_statement;
- BlockNode *const parent;
+ BlockNode* const parent;
const bool is_nested;
bool has_false_statement {false};
- explicit IfStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(false) { }
- explicit IfStatementNode(bool is_nested, BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(is_nested) { }
+ explicit IfStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(false) {}
+ explicit IfStatementNode(bool is_nested, BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(is_nested) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -304,7 +324,31 @@ class IncludeStatementNode : public StatementNode {
public:
const std::string file;
- explicit IncludeStatementNode(const std::string& file, size_t pos) : StatementNode(pos), file(file) { }
+ explicit IncludeStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
+
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
+ }
+};
+
+class ExtendsStatementNode : public StatementNode {
+public:
+ const std::string file;
+
+ explicit ExtendsStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
+
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
+ };
+};
+
+class BlockStatementNode : public StatementNode {
+public:
+ const std::string name;
+ BlockNode block;
+ BlockNode* const parent;
+
+ explicit BlockStatementNode(BlockNode* const parent, const std::string& name, size_t pos): StatementNode(pos), name(name), parent(parent) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
@@ -316,11 +360,11 @@ public:
const std::string key;
ExpressionListNode expression;
- explicit SetStatementNode(const std::string& key, size_t pos) : StatementNode(pos), key(key) { }
+ explicit SetStatementNode(const std::string& key, size_t pos): StatementNode(pos), key(key) {}
void accept(NodeVisitor& v) const {
v.visit(*this);
- };
+ }
};
} // namespace inja
diff --git a/include/inja/parser.hpp b/include/inja/parser.hpp
index 6266c4a..3ea1793 100644
--- a/include/inja/parser.hpp
+++ b/include/inja/parser.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_PARSER_HPP_
#define INCLUDE_INJA_PARSER_HPP_
@@ -7,7 +5,6 @@
#include <stack>
#include <string>
#include <utility>
-#include <queue>
#include <vector>
#include "config.hpp"
@@ -19,19 +16,17 @@
#include "token.hpp"
#include "utils.hpp"
-#include <nlohmann/json.hpp>
-
namespace inja {
/*!
* \brief Class for parsing an inja Template.
*/
class Parser {
- const ParserConfig &config;
+ const ParserConfig& config;
Lexer lexer;
- TemplateStorage &template_storage;
- const FunctionStorage &function_storage;
+ TemplateStorage& template_storage;
+ const FunctionStorage& function_storage;
Token tok, peek_tok;
bool have_peek_tok {false};
@@ -40,21 +35,23 @@ class Parser {
size_t current_bracket_level {0};
size_t current_brace_level {0};
- nonstd::string_view json_literal_start;
+ std::string_view literal_start;
- BlockNode *current_block {nullptr};
- ExpressionListNode *current_expression_list {nullptr};
+ BlockNode* current_block {nullptr};
+ ExpressionListNode* current_expression_list {nullptr};
std::stack<std::pair<FunctionNode*, size_t>> function_stack;
+ std::vector<std::shared_ptr<ExpressionNode>> arguments;
std::stack<std::shared_ptr<FunctionNode>> operator_stack;
std::stack<IfStatementNode*> if_statement_stack;
std::stack<ForStatementNode*> for_statement_stack;
+ std::stack<BlockStatementNode*> block_statement_stack;
- void throw_parser_error(const std::string &message) {
+ inline void throw_parser_error(const std::string& message) const {
INJA_THROW(ParserError(message, lexer.current_position()));
}
- void get_next_token() {
+ inline void get_next_token() {
if (have_peek_tok) {
tok = peek_tok;
have_peek_tok = false;
@@ -63,49 +60,108 @@ class Parser {
}
}
- void get_peek_token() {
+ inline void get_peek_token() {
if (!have_peek_tok) {
peek_tok = lexer.scan();
have_peek_tok = true;
}
}
- void add_json_literal(const char* content_ptr) {
- nonstd::string_view json_text(json_literal_start.data(), tok.text.data() - json_literal_start.data() + tok.text.size());
- current_expression_list->rpn_output.emplace_back(std::make_shared<LiteralNode>(json::parse(json_text), json_text.data() - content_ptr));
+ inline void add_literal(const char* content_ptr) {
+ std::string_view data_text(literal_start.data(), tok.text.data() - literal_start.data() + tok.text.size());
+ arguments.emplace_back(std::make_shared<LiteralNode>(data_text, data_text.data() - content_ptr));
+ }
+
+ inline void add_operator() {
+ auto function = operator_stack.top();
+ operator_stack.pop();
+
+ for (int i = 0; i < function->number_args; ++i) {
+ function->arguments.insert(function->arguments.begin(), arguments.back());
+ arguments.pop_back();
+ }
+ arguments.emplace_back(function);
+ }
+
+ void add_to_template_storage(std::string_view path, std::string& template_name) {
+ if (template_storage.find(template_name) != template_storage.end()) {
+ return;
+ }
+
+ std::string original_path = static_cast<std::string>(path);
+ std::string original_name = template_name;
+
+ if (config.search_included_templates_in_files) {
+ // Build the relative path
+ template_name = original_path + original_name;
+ if (template_name.compare(0, 2, "./") == 0) {
+ template_name.erase(0, 2);
+ }
+
+ if (template_storage.find(template_name) == template_storage.end()) {
+ // Load file
+ std::ifstream file;
+ file.open(template_name);
+ if (!file.fail()) {
+ std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+
+ auto include_template = Template(text);
+ template_storage.emplace(template_name, include_template);
+ parse_into_template(template_storage[template_name], template_name);
+ return;
+ } else if (!config.include_callback) {
+ INJA_THROW(FileError("failed accessing file at '" + template_name + "'"));
+ }
+ }
+ }
+
+ // Try include callback
+ if (config.include_callback) {
+ auto include_template = config.include_callback(original_path, original_name);
+ template_storage.emplace(template_name, include_template);
+ }
+ }
+
+ std::string parse_filename(const Token& tok) const {
+ if (tok.kind != Token::Kind::String) {
+ throw_parser_error("expected string, got '" + tok.describe() + "'");
+ }
+
+ if (tok.text.length() < 2) {
+ throw_parser_error("expected filename, got '" + static_cast<std::string>(tok.text) + "'");
+ }
+
+ // Remove first and last character ""
+ return std::string {tok.text.substr(1, tok.text.length() - 2)};
}
- bool parse_expression(Template &tmpl, Token::Kind closing) {
+ bool parse_expression(Template& tmpl, Token::Kind closing) {
while (tok.kind != closing && tok.kind != Token::Kind::Eof) {
// Literals
switch (tok.kind) {
case Token::Kind::String: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
+ literal_start = tok.text;
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::Number: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
+ literal_start = tok.text;
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::LeftBracket: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
+ literal_start = tok.text;
}
current_bracket_level += 1;
-
} break;
case Token::Kind::LeftBrace: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
+ literal_start = tok.text;
}
current_brace_level += 1;
-
} break;
case Token::Kind::RightBracket: {
if (current_bracket_level == 0) {
@@ -114,9 +170,8 @@ class Parser {
current_bracket_level -= 1;
if (current_brace_level == 0 && current_bracket_level == 0) {
- add_json_literal(tmpl.content.c_str());
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::RightBrace: {
if (current_brace_level == 0) {
@@ -125,35 +180,35 @@ class Parser {
current_brace_level -= 1;
if (current_brace_level == 0 && current_bracket_level == 0) {
- add_json_literal(tmpl.content.c_str());
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::Id: {
get_peek_token();
- // Json Literal
- if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") || tok.text == static_cast<decltype(tok.text)>("null")) {
+ // Data Literal
+ if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") ||
+ tok.text == static_cast<decltype(tok.text)>("null")) {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
+ literal_start = tok.text;
+ add_literal(tmpl.content.c_str());
}
- // Operator
+ // Operator
} else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") {
goto parse_operator;
- // Functions
+ // Functions
} else if (peek_tok.kind == Token::Kind::LeftParen) {
operator_stack.emplace(std::make_shared<FunctionNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
- function_stack.emplace(operator_stack.top().get(), current_paren_level);
+ function_stack.emplace(operator_stack.top().get(), current_paren_level);
- // Variables
+ // Variables
} else {
- current_expression_list->rpn_output.emplace_back(std::make_shared<JsonNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
+ arguments.emplace_back(std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
}
- // Operators
+ // Operators
} break;
case Token::Kind::Equal:
case Token::Kind::NotEqual:
@@ -169,7 +224,7 @@ class Parser {
case Token::Kind::Percent:
case Token::Kind::Dot: {
- parse_operator:
+ parse_operator:
FunctionStorage::Operation operation;
switch (tok.kind) {
case Token::Kind::Id: {
@@ -230,13 +285,14 @@ class Parser {
}
auto function_node = std::make_shared<FunctionNode>(operation, tok.text.data() - tmpl.content.c_str());
- while (!operator_stack.empty() && ((operator_stack.top()->precedence > function_node->precedence) || (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) && (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ while (!operator_stack.empty() &&
+ ((operator_stack.top()->precedence > function_node->precedence) ||
+ (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) &&
+ (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
+ add_operator();
}
operator_stack.emplace(function_node);
-
} break;
case Token::Kind::Comma: {
if (current_brace_level == 0 && current_bracket_level == 0) {
@@ -246,13 +302,11 @@ class Parser {
function_stack.top().first->number_args += 1;
}
-
} break;
case Token::Kind::Colon: {
if (current_brace_level == 0 && current_bracket_level == 0) {
throw_parser_error("unexpected ':'");
}
-
} break;
case Token::Kind::LeftParen: {
current_paren_level += 1;
@@ -264,13 +318,11 @@ class Parser {
function_stack.top().first->number_args = 0;
}
}
-
} break;
case Token::Kind::RightParen: {
current_paren_level -= 1;
while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ add_operator();
}
if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) {
@@ -292,8 +344,7 @@ class Parser {
throw_parser_error("internal error at function " + func->name);
}
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ add_operator();
function_stack.pop();
}
}
@@ -305,14 +356,20 @@ class Parser {
}
while (!operator_stack.empty()) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ add_operator();
+ }
+
+ if (arguments.size() == 1) {
+ current_expression_list->root = arguments[0];
+ arguments = {};
+ } else if (arguments.size() > 1) {
+ throw_parser_error("malformed expression");
}
return true;
}
- bool parse_statement(Template &tmpl, Token::Kind closing, nonstd::string_view path) {
+ bool parse_statement(Template& tmpl, Token::Kind closing, std::string_view path) {
if (tok.kind != Token::Kind::Id) {
return false;
}
@@ -329,12 +386,11 @@ class Parser {
if (!parse_expression(tmpl, closing)) {
return false;
}
-
} else if (tok.text == static_cast<decltype(tok.text)>("else")) {
if (if_statement_stack.empty()) {
throw_parser_error("else without matching if");
}
- auto &if_statement_data = if_statement_stack.top();
+ auto& if_statement_data = if_statement_stack.top();
get_next_token();
if_statement_data->has_false_statement = true;
@@ -354,7 +410,6 @@ class Parser {
return false;
}
}
-
} else if (tok.text == static_cast<decltype(tok.text)>("endif")) {
if (if_statement_stack.empty()) {
throw_parser_error("endif without matching if");
@@ -365,12 +420,40 @@ class Parser {
if_statement_stack.pop();
}
- auto &if_statement_data = if_statement_stack.top();
+ auto& if_statement_data = if_statement_stack.top();
get_next_token();
current_block = if_statement_data->parent;
if_statement_stack.pop();
+ } else if (tok.text == static_cast<decltype(tok.text)>("block")) {
+ get_next_token();
+
+ if (tok.kind != Token::Kind::Id) {
+ throw_parser_error("expected block name, got '" + tok.describe() + "'");
+ }
+
+ const std::string block_name = static_cast<std::string>(tok.text);
+
+ auto block_statement_node = std::make_shared<BlockStatementNode>(current_block, block_name, tok.text.data() - tmpl.content.c_str());
+ current_block->nodes.emplace_back(block_statement_node);
+ block_statement_stack.emplace(block_statement_node.get());
+ current_block = &block_statement_node->block;
+ auto success = tmpl.block_storage.emplace(block_name, block_statement_node);
+ if (!success.second) {
+ throw_parser_error("block with the name '" + block_name + "' does already exist");
+ }
+
+ get_next_token();
+ } else if (tok.text == static_cast<decltype(tok.text)>("endblock")) {
+ if (block_statement_stack.empty()) {
+ throw_parser_error("endblock without matching block");
+ }
+
+ auto& block_statement_data = block_statement_stack.top();
+ get_next_token();
+ current_block = block_statement_data->parent;
+ block_statement_stack.pop();
} else if (tok.text == static_cast<decltype(tok.text)>("for")) {
get_next_token();
@@ -394,11 +477,13 @@ class Parser {
value_token = tok;
get_next_token();
- for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text), static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
+ for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text), static_cast<std::string>(value_token.text),
+ current_block, tok.text.data() - tmpl.content.c_str());
- // Array type
+ // Array type
} else {
- for_statement_node = std::make_shared<ForArrayStatementNode>(static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
+ for_statement_node =
+ std::make_shared<ForArrayStatementNode>(static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
}
current_block->nodes.emplace_back(for_statement_node);
@@ -414,44 +499,34 @@ class Parser {
if (!parse_expression(tmpl, closing)) {
return false;
}
-
} else if (tok.text == static_cast<decltype(tok.text)>("endfor")) {
if (for_statement_stack.empty()) {
throw_parser_error("endfor without matching for");
}
- auto &for_statement_data = for_statement_stack.top();
+ auto& for_statement_data = for_statement_stack.top();
get_next_token();
current_block = for_statement_data->parent;
for_statement_stack.pop();
-
} else if (tok.text == static_cast<decltype(tok.text)>("include")) {
get_next_token();
- if (tok.kind != Token::Kind::String) {
- throw_parser_error("expected string, got '" + tok.describe() + "'");
- }
+ std::string template_name = parse_filename(tok);
+ add_to_template_storage(path, template_name);
- // Build the relative path
- json json_name = json::parse(tok.text);
- std::string pathname = static_cast<std::string>(path);
- pathname += json_name.get_ref<const std::string &>();
- if (pathname.compare(0, 2, "./") == 0) {
- pathname.erase(0, 2);
- }
- // sys::path::remove_dots(pathname, true, sys::path::Style::posix);
+ current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
- if (config.search_included_templates_in_files && template_storage.find(pathname) == template_storage.end()) {
- auto include_template = Template(load_file(pathname));
- template_storage.emplace(pathname, include_template);
- parse_into_template(template_storage[pathname], pathname);
- }
+ get_next_token();
+ } else if (tok.text == static_cast<decltype(tok.text)>("extends")) {
+ get_next_token();
- current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(pathname, tok.text.data() - tmpl.content.c_str()));
+ std::string template_name = parse_filename(tok);
+ add_to_template_storage(path, template_name);
- get_next_token();
+ current_block->nodes.emplace_back(std::make_shared<ExtendsStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
+ get_next_token();
} else if (tok.text == static_cast<decltype(tok.text)>("set")) {
get_next_token();
@@ -474,14 +549,13 @@ class Parser {
if (!parse_expression(tmpl, closing)) {
return false;
}
-
} else {
return false;
}
return true;
}
- void parse_into(Template &tmpl, nonstd::string_view path) {
+ void parse_into(Template& tmpl, std::string_view path) {
lexer.start(tmpl.content);
current_block = &tmpl.root;
@@ -495,7 +569,8 @@ class Parser {
if (!for_statement_stack.empty()) {
throw_parser_error("unmatched for");
}
- } return;
+ }
+ return;
case Token::Kind::Text: {
current_block->nodes.emplace_back(std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
} break;
@@ -545,33 +620,35 @@ class Parser {
}
}
-
public:
- explicit Parser(const ParserConfig &parser_config, const LexerConfig &lexer_config,
- TemplateStorage &template_storage, const FunctionStorage &function_storage)
- : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) { }
+ explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& template_storage,
+ const FunctionStorage& function_storage)
+ : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) {}
- Template parse(nonstd::string_view input, nonstd::string_view path) {
+ Template parse(std::string_view input, std::string_view path) {
auto result = Template(static_cast<std::string>(input));
parse_into(result, path);
return result;
}
- Template parse(nonstd::string_view input) {
+ Template parse(std::string_view input) {
return parse(input, "./");
}
- void parse_into_template(Template& tmpl, nonstd::string_view filename) {
- nonstd::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
+ void parse_into_template(Template& tmpl, std::string_view filename) {
+ std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
// StringRef path = sys::path::parent_path(filename);
auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage);
sub_parser.parse_into(tmpl, path);
}
- std::string load_file(nonstd::string_view filename) {
+ std::string load_file(const std::string& filename) {
std::ifstream file;
- open_file_or_throw(static_cast<std::string>(filename), file);
+ file.open(filename);
+ if (file.fail()) {
+ INJA_THROW(FileError("failed accessing file at '" + filename + "'"));
+ }
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return text;
}
diff --git a/include/inja/renderer.hpp b/include/inja/renderer.hpp
index 7bc518f..f24bb91 100644
--- a/include/inja/renderer.hpp
+++ b/include/inja/renderer.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_RENDERER_HPP_
#define INCLUDE_INJA_RENDERER_HPP_
@@ -9,8 +7,6 @@
#include <utility>
#include <vector>
-#include <nlohmann/json.hpp>
-
#include "config.hpp"
#include "exceptions.hpp"
#include "node.hpp"
@@ -22,25 +18,31 @@ namespace inja {
/*!
* \brief Class for rendering a Template with data.
*/
-class Renderer : public NodeVisitor {
+class Renderer : public NodeVisitor {
using Op = FunctionStorage::Operation;
const RenderConfig config;
- const Template *current_template;
- const TemplateStorage &template_storage;
- const FunctionStorage &function_storage;
+ const TemplateStorage& template_storage;
+ const FunctionStorage& function_storage;
+
+ const Template* current_template;
+ size_t current_level {0};
+ std::vector<const Template*> template_stack;
+ std::vector<const BlockStatementNode*> block_statement_stack;
- const json *json_input;
- std::ostream *output_stream;
+ const json* data_input;
+ std::ostream* output_stream;
- json json_additional_data;
- json* current_loop_data = &json_additional_data["loop"];
+ json additional_data;
+ json* current_loop_data = &additional_data["loop"];
- std::vector<std::shared_ptr<json>> json_tmp_stack;
- std::stack<const json*> json_eval_stack;
- std::stack<const JsonNode*> not_found_stack;
+ std::vector<std::shared_ptr<json>> data_tmp_stack;
+ std::stack<const json*> data_eval_stack;
+ std::stack<const DataNode*> not_found_stack;
- bool truthy(const json* data) const {
+ bool break_rendering {false};
+
+ static bool truthy(const json* data) {
if (data->is_boolean()) {
return data->get<bool>();
} else if (data->is_number()) {
@@ -51,7 +53,7 @@ class Renderer : public NodeVisitor {
return !data->empty();
}
- void print_json(const std::shared_ptr<json> value) {
+ void print_data(const std::shared_ptr<json> value) {
if (value->is_string()) {
*output_stream << value->get_ref<const json::string_t&>();
} else if (value->is_number_integer()) {
@@ -63,18 +65,20 @@ class Renderer : public NodeVisitor {
}
const std::shared_ptr<json> eval_expression_list(const ExpressionListNode& expression_list) {
- for (auto& expression : expression_list.rpn_output) {
- expression->accept(*this);
+ if (!expression_list.root) {
+ throw_renderer_error("empty expression", expression_list);
}
- if (json_eval_stack.empty()) {
+ expression_list.root->accept(*this);
+
+ if (data_eval_stack.empty()) {
throw_renderer_error("empty expression", expression_list);
- } else if (json_eval_stack.size() != 1) {
+ } else if (data_eval_stack.size() != 1) {
throw_renderer_error("malformed expression", expression_list);
}
- auto result = json_eval_stack.top();
- json_eval_stack.pop();
+ const auto result = data_eval_stack.top();
+ data_eval_stack.pop();
if (!result) {
if (not_found_stack.empty()) {
@@ -89,51 +93,68 @@ class Renderer : public NodeVisitor {
return std::make_shared<json>(*result);
}
- void throw_renderer_error(const std::string &message, const AstNode& node) {
+ void throw_renderer_error(const std::string& message, const AstNode& node) {
SourceLocation loc = get_source_location(current_template->content, node.pos);
INJA_THROW(RenderError(message, loc));
}
- template<size_t N, bool throw_not_found=true>
- std::array<const json*, N> get_arguments(const AstNode& node) {
- if (json_eval_stack.size() < N) {
- throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node);
+ void make_result(const json&& result) {
+ auto result_ptr = std::make_shared<json>(result);
+ data_tmp_stack.push_back(result_ptr);
+ data_eval_stack.push(result_ptr.get());
+ }
+
+ template <size_t N, size_t N_start = 0, bool throw_not_found = true> std::array<const json*, N> get_arguments(const FunctionNode& node) {
+ if (node.arguments.size() < N_start + N) {
+ throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " + std::to_string(node.arguments.size()), node);
+ }
+
+ for (size_t i = N_start; i < N_start + N; i += 1) {
+ node.arguments[i]->accept(*this);
+ }
+
+ if (data_eval_stack.size() < N) {
+ throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
}
std::array<const json*, N> result;
for (size_t i = 0; i < N; i += 1) {
- result[N - i - 1] = json_eval_stack.top();
- json_eval_stack.pop();
+ result[N - i - 1] = data_eval_stack.top();
+ data_eval_stack.pop();
if (!result[N - i - 1]) {
- auto json_node = not_found_stack.top();
+ const auto data_node = not_found_stack.top();
not_found_stack.pop();
if (throw_not_found) {
- throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
+ throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
}
}
}
return result;
}
- template<bool throw_not_found=true>
- Arguments get_argument_vector(size_t N, const AstNode& node) {
- if (json_eval_stack.size() < N) {
- throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node);
+ template <bool throw_not_found = true> Arguments get_argument_vector(const FunctionNode& node) {
+ const size_t N = node.arguments.size();
+ for (auto a : node.arguments) {
+ a->accept(*this);
+ }
+
+ if (data_eval_stack.size() < N) {
+ throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
}
Arguments result {N};
for (size_t i = 0; i < N; i += 1) {
- result[N - i - 1] = json_eval_stack.top();
- json_eval_stack.pop();
+ result[N - i - 1] = data_eval_stack.top();
+ data_eval_stack.pop();
if (!result[N - i - 1]) {
- auto json_node = not_found_stack.top();
+ const auto data_node = not_found_stack.top();
not_found_stack.pop();
if (throw_not_found) {
- throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
+ throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
}
}
}
@@ -143,6 +164,10 @@ class Renderer : public NodeVisitor {
void visit(const BlockNode& node) {
for (auto& n : node.nodes) {
n->accept(*this);
+
+ if (break_rendering) {
+ break;
+ }
}
}
@@ -150,325 +175,294 @@ class Renderer : public NodeVisitor {
output_stream->write(current_template->content.c_str() + node.pos, node.length);
}
- void visit(const ExpressionNode&) { }
+ void visit(const ExpressionNode&) {}
void visit(const LiteralNode& node) {
- json_eval_stack.push(&node.value);
+ data_eval_stack.push(&node.value);
}
- void visit(const JsonNode& node) {
- if (json_additional_data.contains(node.ptr)) {
- json_eval_stack.push(&(json_additional_data[node.ptr]));
-
- } else if (json_input->contains(node.ptr)) {
- json_eval_stack.push(&(*json_input)[node.ptr]);
-
+ void visit(const DataNode& node) {
+ if (additional_data.contains(node.ptr)) {
+ data_eval_stack.push(&(additional_data[node.ptr]));
+ } else if (data_input->contains(node.ptr)) {
+ data_eval_stack.push(&(*data_input)[node.ptr]);
} else {
// Try to evaluate as a no-argument callback
- auto function_data = function_storage.find_function(node.name, 0);
+ const auto function_data = function_storage.find_function(node.name, 0);
if (function_data.operation == FunctionStorage::Operation::Callback) {
Arguments empty_args {};
- auto value = std::make_shared<json>(function_data.callback(empty_args));
- json_tmp_stack.push_back(value);
- json_eval_stack.push(value.get());
-
+ const auto value = std::make_shared<json>(function_data.callback(empty_args));
+ data_tmp_stack.push_back(value);
+ data_eval_stack.push(value.get());
} else {
- json_eval_stack.push(nullptr);
+ data_eval_stack.push(nullptr);
not_found_stack.emplace(&node);
}
}
}
void visit(const FunctionNode& node) {
- std::shared_ptr<json> result_ptr;
-
switch (node.operation) {
case Op::Not: {
- auto args = get_arguments<1>(node);
- result_ptr = std::make_shared<json>(!truthy(args[0]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<1>(node);
+ make_result(!truthy(args[0]));
} break;
case Op::And: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(truthy(args[0]) && truthy(args[1]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0]));
} break;
case Op::Or: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(truthy(args[0]) || truthy(args[1]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0]));
} break;
case Op::In: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
} break;
case Op::Equal: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] == *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] == *args[1]);
} break;
case Op::NotEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] != *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] != *args[1]);
} break;
case Op::Greater: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] > *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] > *args[1]);
} break;
case Op::GreaterEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] >= *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] >= *args[1]);
} break;
case Op::Less: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] < *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] < *args[1]);
} break;
case Op::LessEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] <= *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] <= *args[1]);
} break;
case Op::Add: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_string() && args[1]->is_string()) {
- result_ptr = std::make_shared<json>(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
} else if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() + args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<int>() + args[1]->get<int>());
} else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() + args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<double>() + args[1]->get<double>());
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Subtract: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() - args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<int>() - args[1]->get<int>());
} else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() - args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<double>() - args[1]->get<double>());
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Multiplication: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() * args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<int>() * args[1]->get<int>());
} else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() * args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<double>() * args[1]->get<double>());
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Division: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[1]->get<double>() == 0) {
throw_renderer_error("division by zero", node);
}
- result_ptr = std::make_shared<json>(args[0]->get<double>() / args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(args[0]->get<double>() / args[1]->get<double>());
} break;
case Op::Power: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_number_integer() && args[1]->get<int>() >= 0) {
- int result = std::pow(args[0]->get<int>(), args[1]->get<int>());
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
+ int result = static_cast<int>(std::pow(args[0]->get<int>(), args[1]->get<int>()));
+ make_result(result);
} else {
double result = std::pow(args[0]->get<double>(), args[1]->get<int>());
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
+ make_result(result);
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Modulo: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(args[0]->get<int>() % args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(args[0]->get<int>() % args[1]->get<int>());
} break;
case Op::AtId: {
- json_eval_stack.pop(); // Pop id nullptr
- auto container = get_arguments<1, false>(node)[0];
+ const auto container = get_arguments<1, 0, false>(node)[0];
+ node.arguments[1]->accept(*this);
if (not_found_stack.empty()) {
throw_renderer_error("could not find element with given name", node);
}
- auto id_node = not_found_stack.top();
+ const auto id_node = not_found_stack.top();
not_found_stack.pop();
- json_eval_stack.push(&container->at(id_node->name));
+ data_eval_stack.pop();
+ data_eval_stack.push(&container->at(id_node->name));
} break;
case Op::At: {
- auto args = get_arguments<2>(node);
- json_eval_stack.push(&args[0]->at(args[1]->get<int>()));
+ const auto args = get_arguments<2>(node);
+ if (args[0]->is_object()) {
+ data_eval_stack.push(&args[0]->at(args[1]->get<std::string>()));
+ } else {
+ data_eval_stack.push(&args[0]->at(args[1]->get<int>()));
+ }
} break;
case Op::Default: {
- auto default_arg = get_arguments<1>(node)[0];
- auto test_arg = get_arguments<1, false>(node)[0];
- json_eval_stack.push(test_arg ? test_arg : default_arg);
+ const auto test_arg = get_arguments<1, 0, false>(node)[0];
+ data_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]);
} break;
case Op::DivisibleBy: {
- auto args = get_arguments<2>(node);
- int divisor = args[1]->get<int>();
- result_ptr = std::make_shared<json>((divisor != 0) && (args[0]->get<int>() % divisor == 0));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ const int divisor = args[1]->get<int>();
+ make_result((divisor != 0) && (args[0]->get<int>() % divisor == 0));
} break;
case Op::Even: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
} break;
case Op::Exists: {
- auto &&name = get_arguments<1>(node)[0]->get_ref<const std::string &>();
- result_ptr = std::make_shared<json>(json_input->contains(json::json_pointer(JsonNode::convert_dot_to_json_ptr(name))));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ auto&& name = get_arguments<1>(node)[0]->get_ref<const std::string&>();
+ make_result(data_input->contains(json::json_pointer(DataNode::convert_dot_to_ptr(name))));
} break;
case Op::ExistsInObject: {
- auto args = get_arguments<2>(node);
- auto &&name = args[1]->get_ref<const std::string &>();
- result_ptr = std::make_shared<json>(args[0]->find(name) != args[0]->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ auto&& name = args[1]->get_ref<const std::string&>();
+ make_result(args[0]->find(name) != args[0]->end());
} break;
case Op::First: {
- auto result = &get_arguments<1>(node)[0]->front();
- json_eval_stack.push(result);
+ const auto result = &get_arguments<1>(node)[0]->front();
+ data_eval_stack.push(result);
} break;
case Op::Float: {
- result_ptr = std::make_shared<json>(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
} break;
case Op::Int: {
- result_ptr = std::make_shared<json>(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
} break;
case Op::Last: {
- auto result = &get_arguments<1>(node)[0]->back();
- json_eval_stack.push(result);
+ const auto result = &get_arguments<1>(node)[0]->back();
+ data_eval_stack.push(result);
} break;
case Op::Length: {
- auto val = get_arguments<1>(node)[0];
+ const auto val = get_arguments<1>(node)[0];
if (val->is_string()) {
- result_ptr = std::make_shared<json>(val->get_ref<const std::string &>().length());
+ make_result(val->get_ref<const std::string&>().length());
} else {
- result_ptr = std::make_shared<json>(val->size());
+ make_result(val->size());
}
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Lower: {
std::string result = get_arguments<1>(node)[0]->get<std::string>();
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::move(result));
} break;
case Op::Max: {
- auto args = get_arguments<1>(node);
- auto result = std::max_element(args[0]->begin(), args[0]->end());
- json_eval_stack.push(&(*result));
+ const auto args = get_arguments<1>(node);
+ const auto result = std::max_element(args[0]->begin(), args[0]->end());
+ data_eval_stack.push(&(*result));
} break;
case Op::Min: {
- auto args = get_arguments<1>(node);
- auto result = std::min_element(args[0]->begin(), args[0]->end());
- json_eval_stack.push(&(*result));
+ const auto args = get_arguments<1>(node);
+ const auto result = std::min_element(args[0]->begin(), args[0]->end());
+ data_eval_stack.push(&(*result));
} break;
case Op::Odd: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
} break;
case Op::Range: {
std::vector<int> result(get_arguments<1>(node)[0]->get<int>());
std::iota(result.begin(), result.end(), 0);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::move(result));
} break;
case Op::Round: {
- auto args = get_arguments<2>(node);
- int precision = args[1]->get<int>();
- double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ const int precision = args[1]->get<int>();
+ const double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
+ if (precision == 0) {
+ make_result(int(result));
+ } else {
+ make_result(result);
+ }
} break;
case Op::Sort: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
+ auto result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
std::sort(result_ptr->begin(), result_ptr->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ data_tmp_stack.push_back(result_ptr);
+ data_eval_stack.push(result_ptr.get());
} break;
case Op::Upper: {
std::string result = get_arguments<1>(node)[0]->get<std::string>();
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::move(result));
} break;
case Op::IsBoolean: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_boolean());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_boolean());
} break;
case Op::IsNumber: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_number());
} break;
case Op::IsInteger: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_integer());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_number_integer());
} break;
case Op::IsFloat: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_float());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_number_float());
} break;
case Op::IsObject: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_object());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_object());
} break;
case Op::IsArray: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_array());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_array());
} break;
case Op::IsString: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_string());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_string());
} break;
case Op::Callback: {
- auto args = get_argument_vector(node.number_args, node);
- result_ptr = std::make_shared<json>(node.callback(args));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ auto args = get_argument_vector(node);
+ make_result(node.callback(args));
+ } break;
+ case Op::Super: {
+ const auto args = get_argument_vector(node);
+ const size_t old_level = current_level;
+ const size_t level_diff = (args.size() == 1) ? args[0]->get<int>() : 1;
+ const size_t level = current_level + level_diff;
+
+ if (block_statement_stack.empty()) {
+ throw_renderer_error("super() call is not within a block", node);
+ }
+
+ if (level < 1 || level > template_stack.size() - 1) {
+ throw_renderer_error("level of super() call does not match parent templates (between 1 and " + std::to_string(template_stack.size() - 1) + ")", node);
+ }
+
+ const auto current_block_statement = block_statement_stack.back();
+ const Template* new_template = template_stack.at(level);
+ const Template* old_template = current_template;
+ const auto block_it = new_template->block_storage.find(current_block_statement->name);
+ if (block_it != new_template->block_storage.end()) {
+ current_template = new_template;
+ current_level = level;
+ block_it->second->block.accept(*this);
+ current_level = old_level;
+ current_template = old_template;
+ } else {
+ throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node);
+ }
+ make_result(nullptr);
+ } break;
+ case Op::Join: {
+ const auto args = get_arguments<2>(node);
+ const auto separator = args[1]->get<std::string>();
+ std::ostringstream os;
+ std::string sep;
+ for (const auto& value : *args[0]) {
+ os << sep;
+ if (value.is_string()) {
+ os << value.get<std::string>(); // otherwise the value is surrounded with ""
+ } else {
+ os << value;
+ }
+ sep = separator;
+ }
+ make_result(os.str());
} break;
case Op::ParenLeft:
case Op::ParenRight:
@@ -478,15 +472,15 @@ class Renderer : public NodeVisitor {
}
void visit(const ExpressionListNode& node) {
- print_json(eval_expression_list(node));
+ print_data(eval_expression_list(node));
}
- void visit(const StatementNode&) { }
+ void visit(const StatementNode&) {}
- void visit(const ForStatementNode&) { }
+ void visit(const ForStatementNode&) {}
void visit(const ForArrayStatementNode& node) {
- auto result = eval_expression_list(node.condition);
+ const auto result = eval_expression_list(node.condition);
if (!result->is_array()) {
throw_renderer_error("object must be an array", node);
}
@@ -500,7 +494,7 @@ class Renderer : public NodeVisitor {
(*current_loop_data)["is_first"] = true;
(*current_loop_data)["is_last"] = (result->size() <= 1);
for (auto it = result->begin(); it != result->end(); ++it) {
- json_additional_data[static_cast<std::string>(node.value)] = *it;
+ additional_data[static_cast<std::string>(node.value)] = *it;
(*current_loop_data)["index"] = index;
(*current_loop_data)["index1"] = index + 1;
@@ -515,17 +509,17 @@ class Renderer : public NodeVisitor {
++index;
}
- json_additional_data[static_cast<std::string>(node.value)].clear();
+ additional_data[static_cast<std::string>(node.value)].clear();
if (!(*current_loop_data)["parent"].empty()) {
- auto tmp = (*current_loop_data)["parent"];
+ const auto tmp = (*current_loop_data)["parent"];
*current_loop_data = std::move(tmp);
} else {
- current_loop_data = &json_additional_data["loop"];
+ current_loop_data = &additional_data["loop"];
}
}
void visit(const ForObjectStatementNode& node) {
- auto result = eval_expression_list(node.condition);
+ const auto result = eval_expression_list(node.condition);
if (!result->is_object()) {
throw_renderer_error("object must be an object", node);
}
@@ -538,8 +532,8 @@ class Renderer : public NodeVisitor {
(*current_loop_data)["is_first"] = true;
(*current_loop_data)["is_last"] = (result->size() <= 1);
for (auto it = result->begin(); it != result->end(); ++it) {
- json_additional_data[static_cast<std::string>(node.key)] = it.key();
- json_additional_data[static_cast<std::string>(node.value)] = it.value();
+ additional_data[static_cast<std::string>(node.key)] = it.key();
+ additional_data[static_cast<std::string>(node.value)] = it.value();
(*current_loop_data)["index"] = index;
(*current_loop_data)["index1"] = index + 1;
@@ -554,17 +548,17 @@ class Renderer : public NodeVisitor {
++index;
}
- json_additional_data[static_cast<std::string>(node.key)].clear();
- json_additional_data[static_cast<std::string>(node.value)].clear();
+ additional_data[static_cast<std::string>(node.key)].clear();
+ additional_data[static_cast<std::string>(node.value)].clear();
if (!(*current_loop_data)["parent"].empty()) {
*current_loop_data = std::move((*current_loop_data)["parent"]);
} else {
- current_loop_data = &json_additional_data["loop"];
+ current_loop_data = &additional_data["loop"];
}
}
void visit(const IfStatementNode& node) {
- auto result = eval_expression_list(node.condition);
+ const auto result = eval_expression_list(node.condition);
if (truthy(result.get())) {
node.true_statement.accept(*this);
} else if (node.has_false_statement) {
@@ -574,35 +568,63 @@ class Renderer : public NodeVisitor {
void visit(const IncludeStatementNode& node) {
auto sub_renderer = Renderer(config, template_storage, function_storage);
- auto included_template_it = template_storage.find(node.file);
-
+ const auto included_template_it = template_storage.find(node.file);
if (included_template_it != template_storage.end()) {
- sub_renderer.render_to(*output_stream, included_template_it->second, *json_input, &json_additional_data);
+ sub_renderer.render_to(*output_stream, included_template_it->second, *data_input, &additional_data);
} else if (config.throw_at_missing_includes) {
throw_renderer_error("include '" + node.file + "' not found", node);
}
}
+ void visit(const ExtendsStatementNode& node) {
+ const auto included_template_it = template_storage.find(node.file);
+ if (included_template_it != template_storage.end()) {
+ const Template* parent_template = &included_template_it->second;
+ render_to(*output_stream, *parent_template, *data_input, &additional_data);
+ break_rendering = true;
+ } else if (config.throw_at_missing_includes) {
+ throw_renderer_error("extends '" + node.file + "' not found", node);
+ }
+ }
+
+ void visit(const BlockStatementNode& node) {
+ const size_t old_level = current_level;
+ current_level = 0;
+ current_template = template_stack.front();
+ const auto block_it = current_template->block_storage.find(node.name);
+ if (block_it != current_template->block_storage.end()) {
+ block_statement_stack.emplace_back(&node);
+ block_it->second->block.accept(*this);
+ block_statement_stack.pop_back();
+ }
+ current_level = old_level;
+ current_template = template_stack.back();
+ }
+
void visit(const SetStatementNode& node) {
- json_additional_data[node.key] = *eval_expression_list(node.expression);
+ std::string ptr = node.key;
+ replace_substring(ptr, ".", "/");
+ ptr = "/" + ptr;
+ additional_data[json::json_pointer(ptr)] = *eval_expression_list(node.expression);
}
public:
- Renderer(const RenderConfig& config, const TemplateStorage &template_storage, const FunctionStorage &function_storage)
- : config(config), template_storage(template_storage), function_storage(function_storage) { }
+ Renderer(const RenderConfig& config, const TemplateStorage& template_storage, const FunctionStorage& function_storage)
+ : config(config), template_storage(template_storage), function_storage(function_storage) {}
- void render_to(std::ostream &os, const Template &tmpl, const json &data, json *loop_data = nullptr) {
+ void render_to(std::ostream& os, const Template& tmpl, const json& data, json* loop_data = nullptr) {
output_stream = &os;
current_template = &tmpl;
- json_input = &data;
+ data_input = &data;
if (loop_data) {
- json_additional_data = *loop_data;
- current_loop_data = &json_additional_data["loop"];
+ additional_data = *loop_data;
+ current_loop_data = &additional_data["loop"];
}
+ template_stack.emplace_back(current_template);
current_template->root.accept(*this);
- json_tmp_stack.clear();
+ data_tmp_stack.clear();
}
};
diff --git a/include/inja/statistics.hpp b/include/inja/statistics.hpp
index 71fc719..da33593 100644
--- a/include/inja/statistics.hpp
+++ b/include/inja/statistics.hpp
@@ -1,11 +1,8 @@
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_STATISTICS_HPP_
#define INCLUDE_INJA_STATISTICS_HPP_
#include "node.hpp"
-
namespace inja {
/*!
@@ -18,24 +15,26 @@ class StatisticsVisitor : public NodeVisitor {
}
}
- void visit(const TextNode&) { }
- void visit(const ExpressionNode&) { }
- void visit(const LiteralNode&) { }
+ void visit(const TextNode&) {}
+ void visit(const ExpressionNode&) {}
+ void visit(const LiteralNode&) {}
- void visit(const JsonNode&) {
+ void visit(const DataNode&) {
variable_counter += 1;
}
- void visit(const FunctionNode&) { }
-
- void visit(const ExpressionListNode& node) {
- for (auto& n : node.rpn_output) {
+ void visit(const FunctionNode& node) {
+ for (auto& n : node.arguments) {
n->accept(*this);
}
}
- void visit(const StatementNode&) { }
- void visit(const ForStatementNode&) { }
+ void visit(const ExpressionListNode& node) {
+ node.root->accept(*this);
+ }
+
+ void visit(const StatementNode&) {}
+ void visit(const ForStatementNode&) {}
void visit(const ForArrayStatementNode& node) {
node.condition.accept(*this);
@@ -53,14 +52,20 @@ class StatisticsVisitor : public NodeVisitor {
node.false_statement.accept(*this);
}
- void visit(const IncludeStatementNode&) { }
+ void visit(const IncludeStatementNode&) {}
+
+ void visit(const ExtendsStatementNode&) {}
+
+ void visit(const BlockStatementNode& node) {
+ node.block.accept(*this);
+ }
- void visit(const SetStatementNode&) { }
+ void visit(const SetStatementNode&) {}
public:
unsigned int variable_counter;
- explicit StatisticsVisitor() : variable_counter(0) { }
+ explicit StatisticsVisitor(): variable_counter(0) {}
};
} // namespace inja
diff --git a/include/inja/string_view.hpp b/include/inja/string_view.hpp
deleted file mode 100644
index 2bb50c9..0000000
--- a/include/inja/string_view.hpp
+++ /dev/null
@@ -1,1416 +0,0 @@
-// Copyright 2017-2019 by Martin Moene
-//
-// string-view lite, a C++17-like string_view for C++98 and later.
-// For more information see https://github.com/martinmoene/string-view-lite
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-#pragma once
-
-#ifndef NONSTD_SV_LITE_H_INCLUDED
-#define NONSTD_SV_LITE_H_INCLUDED
-
-#define string_view_lite_MAJOR 1
-#define string_view_lite_MINOR 4
-#define string_view_lite_PATCH 0
-
-#define string_view_lite_VERSION \
- nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY( \
- string_view_lite_PATCH)
-
-#define nssv_STRINGIFY(x) nssv_STRINGIFY_(x)
-#define nssv_STRINGIFY_(x) #x
-
-// string-view lite configuration:
-
-#define nssv_STRING_VIEW_DEFAULT 0
-#define nssv_STRING_VIEW_NONSTD 1
-#define nssv_STRING_VIEW_STD 2
-
-#if !defined(nssv_CONFIG_SELECT_STRING_VIEW)
-#define nssv_CONFIG_SELECT_STRING_VIEW (nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD)
-#endif
-
-#if defined(nssv_CONFIG_SELECT_STD_STRING_VIEW) || defined(nssv_CONFIG_SELECT_NONSTD_STRING_VIEW)
-#error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
-#endif
-
-#ifndef nssv_CONFIG_STD_SV_OPERATOR
-#define nssv_CONFIG_STD_SV_OPERATOR 0
-#endif
-
-#ifndef nssv_CONFIG_USR_SV_OPERATOR
-#define nssv_CONFIG_USR_SV_OPERATOR 1
-#endif
-
-#ifdef nssv_CONFIG_CONVERSION_STD_STRING
-#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
-#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
-#endif
-
-#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
-#endif
-
-#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
-#endif
-
-// Control presence of exception handling (try and auto discover):
-
-#ifndef nssv_CONFIG_NO_EXCEPTIONS
-#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
-#define nssv_CONFIG_NO_EXCEPTIONS 0
-#else
-#define nssv_CONFIG_NO_EXCEPTIONS 1
-#endif
-#endif
-
-// C++ language version detection (C++20 is speculative):
-// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
-
-#ifndef nssv_CPLUSPLUS
-#if defined(_MSVC_LANG) && !defined(__clang__)
-#define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
-#else
-#define nssv_CPLUSPLUS __cplusplus
-#endif
-#endif
-
-#define nssv_CPP98_OR_GREATER (nssv_CPLUSPLUS >= 199711L)
-#define nssv_CPP11_OR_GREATER (nssv_CPLUSPLUS >= 201103L)
-#define nssv_CPP11_OR_GREATER_ (nssv_CPLUSPLUS >= 201103L)
-#define nssv_CPP14_OR_GREATER (nssv_CPLUSPLUS >= 201402L)
-#define nssv_CPP17_OR_GREATER (nssv_CPLUSPLUS >= 201703L)
-#define nssv_CPP20_OR_GREATER (nssv_CPLUSPLUS >= 202000L)
-
-// use C++17 std::string_view if available and requested:
-
-#if nssv_CPP17_OR_GREATER && defined(__has_include)
-#if __has_include(<string_view> )
-#define nssv_HAVE_STD_STRING_VIEW 1
-#else
-#define nssv_HAVE_STD_STRING_VIEW 0
-#endif
-#else
-#define nssv_HAVE_STD_STRING_VIEW 0
-#endif
-
-#define nssv_USES_STD_STRING_VIEW \
- ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || \
- ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW))
-
-#define nssv_HAVE_STARTS_WITH (nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW)
-#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
-
-//
-// Use C++17 std::string_view:
-//
-
-#if nssv_USES_STD_STRING_VIEW
-
-#include <string_view>
-
-// Extensions for std::string:
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-
-template <class CharT, class Traits, class Allocator = std::allocator<CharT>>
-std::basic_string<CharT, Traits, Allocator> to_string(std::basic_string_view<CharT, Traits> v,
- Allocator const &a = Allocator()) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
-}
-
-template <class CharT, class Traits, class Allocator>
-std::basic_string_view<CharT, Traits> to_string_view(std::basic_string<CharT, Traits, Allocator> const &s) {
- return std::basic_string_view<CharT, Traits>(s.data(), s.size());
-}
-
-// Literal operators sv and _sv:
-
-#if nssv_CONFIG_STD_SV_OPERATOR
-
-using namespace std::literals::string_view_literals;
-
-#endif
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-
-inline namespace literals {
-inline namespace string_view_literals {
-
-constexpr std::string_view operator"" _sv(const char *str, size_t len) noexcept // (1)
-{
- return std::string_view {str, len};
-}
-
-constexpr std::u16string_view operator"" _sv(const char16_t *str, size_t len) noexcept // (2)
-{
- return std::u16string_view {str, len};
-}
-
-constexpr std::u32string_view operator"" _sv(const char32_t *str, size_t len) noexcept // (3)
-{
- return std::u32string_view {str, len};
-}
-
-constexpr std::wstring_view operator"" _sv(const wchar_t *str, size_t len) noexcept // (4)
-{
- return std::wstring_view {str, len};
-}
-
-} // namespace string_view_literals
-} // namespace literals
-
-#endif // nssv_CONFIG_USR_SV_OPERATOR
-
-} // namespace nonstd
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-
-using std::basic_string_view;
-using std::string_view;
-using std::u16string_view;
-using std::u32string_view;
-using std::wstring_view;
-
-// literal "sv" and "_sv", see above
-
-using std::operator==;
-using std::operator!=;
-using std::operator<;
-using std::operator<=;
-using std::operator>;
-using std::operator>=;
-
-using std::operator<<;
-
-} // namespace nonstd
-
-#else // nssv_HAVE_STD_STRING_VIEW
-
-//
-// Before C++17: use string_view lite:
-//
-
-// Compiler versions:
-//
-// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0)
-// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002)
-// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003)
-// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
-// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
-// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
-// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
-// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
-// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
-// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
-
-#if defined(_MSC_VER) && !defined(__clang__)
-#define nssv_COMPILER_MSVC_VER (_MSC_VER)
-#define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900)))
-#else
-#define nssv_COMPILER_MSVC_VER 0
-#define nssv_COMPILER_MSVC_VERSION 0
-#endif
-
-#define nssv_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch))
-
-#if defined(__clang__)
-#define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
-#else
-#define nssv_COMPILER_CLANG_VERSION 0
-#endif
-
-#if defined(__GNUC__) && !defined(__clang__)
-#define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
-#else
-#define nssv_COMPILER_GNUC_VERSION 0
-#endif
-
-// half-open range [lo..hi):
-#define nssv_BETWEEN(v, lo, hi) ((lo) <= (v) && (v) < (hi))
-
-// Presence of language and library features:
-
-#ifdef _HAS_CPP0X
-#define nssv_HAS_CPP0X _HAS_CPP0X
-#else
-#define nssv_HAS_CPP0X 0
-#endif
-
-// Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
-
-#if nssv_COMPILER_MSVC_VER >= 1900
-#undef nssv_CPP11_OR_GREATER
-#define nssv_CPP11_OR_GREATER 1
-#endif
-
-#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
-#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
-#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
-#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
-#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
-#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
-
-#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
-#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
-
-// Presence of C++11 language features:
-
-#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
-#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
-#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
-#define nssv_HAVE_NOEXCEPT nssv_CPP11_140
-#define nssv_HAVE_NULLPTR nssv_CPP11_100
-#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
-#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
-#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
-#define nssv_HAVE_WCHAR16_T nssv_CPP11_100
-#define nssv_HAVE_WCHAR32_T nssv_CPP11_100
-
-#if !((nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION) || nssv_BETWEEN(nssv_COMPILER_CLANG_VERSION, 300, 400))
-#define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
-#else
-#define nssv_HAVE_STD_DEFINED_LITERALS 0
-#endif
-
-// Presence of C++14 language features:
-
-#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
-
-// Presence of C++17 language features:
-
-#define nssv_HAVE_NODISCARD nssv_CPP17_000
-
-// Presence of C++ library features:
-
-#define nssv_HAVE_STD_HASH nssv_CPP11_120
-
-// C++ feature usage:
-
-#if nssv_HAVE_CONSTEXPR_11
-#define nssv_constexpr constexpr
-#else
-#define nssv_constexpr /*constexpr*/
-#endif
-
-#if nssv_HAVE_CONSTEXPR_14
-#define nssv_constexpr14 constexpr
-#else
-#define nssv_constexpr14 /*constexpr*/
-#endif
-
-#if nssv_HAVE_EXPLICIT_CONVERSION
-#define nssv_explicit explicit
-#else
-#define nssv_explicit /*explicit*/
-#endif
-
-#if nssv_HAVE_INLINE_NAMESPACE
-#define nssv_inline_ns inline
-#else
-#define nssv_inline_ns /*inline*/
-#endif
-
-#if nssv_HAVE_NOEXCEPT
-#define nssv_noexcept noexcept
-#else
-#define nssv_noexcept /*noexcept*/
-#endif
-
-//#if nssv_HAVE_REF_QUALIFIER
-//# define nssv_ref_qual &
-//# define nssv_refref_qual &&
-//#else
-//# define nssv_ref_qual /*&*/
-//# define nssv_refref_qual /*&&*/
-//#endif
-
-#if nssv_HAVE_NULLPTR
-#define nssv_nullptr nullptr
-#else
-#define nssv_nullptr NULL
-#endif
-
-#if nssv_HAVE_NODISCARD
-#define nssv_nodiscard [[nodiscard]]
-#else
-#define nssv_nodiscard /*[[nodiscard]]*/
-#endif
-
-// Additional includes:
-
-#include <algorithm>
-#include <cassert>
-#include <iterator>
-#include <limits>
-#include <ostream>
-#include <string> // std::char_traits<>
-
-#if !nssv_CONFIG_NO_EXCEPTIONS
-#include <stdexcept>
-#endif
-
-#if nssv_CPP11_OR_GREATER
-#include <type_traits>
-#endif
-
-// Clang, GNUC, MSVC warning suppression macros:
-
-#if defined(__clang__)
-#pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wuser-defined-literals"
-#elif defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wliteral-suffix"
-#endif // __clang__
-
-#if nssv_COMPILER_MSVC_VERSION >= 140
-#define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
-#define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress : code))
-#define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable : codes))
-#else
-#define nssv_SUPPRESS_MSGSL_WARNING(expr)
-#define nssv_SUPPRESS_MSVC_WARNING(code, descr)
-#define nssv_DISABLE_MSVC_WARNINGS(codes)
-#endif
-
-#if defined(__clang__)
-#define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
-#elif defined(__GNUC__)
-#define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
-#elif nssv_COMPILER_MSVC_VERSION >= 140
-#define nssv_RESTORE_WARNINGS() __pragma(warning(pop))
-#else
-#define nssv_RESTORE_WARNINGS()
-#endif
-
-// Suppress the following MSVC (GSL) warnings:
-// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not
-// start with an underscore are reserved
-// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
-// use brace initialization, gsl::narrow_cast or gsl::narow
-// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
-
-nssv_DISABLE_MSVC_WARNINGS(4455 26481 26472)
- // nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
- // nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
-
- namespace nonstd {
- namespace sv_lite {
-
-#if nssv_CPP11_OR_GREATER
-
- namespace detail {
-
- // Expect tail call optimization to make length() non-recursive:
-
- template <typename CharT> inline constexpr std::size_t length(CharT *s, std::size_t result = 0) {
- return *s == '\0' ? result : length(s + 1, result + 1);
- }
-
- } // namespace detail
-
-#endif // nssv_CPP11_OR_GREATER
-
- template <class CharT, class Traits = std::char_traits<CharT>> class basic_string_view;
-
- //
- // basic_string_view:
- //
-
- template <class CharT, class Traits /* = std::char_traits<CharT> */
- >
- class basic_string_view {
- public:
- // Member types:
-
- typedef Traits traits_type;
- typedef CharT value_type;
-
- typedef CharT *pointer;
- typedef CharT const *const_pointer;
- typedef CharT &reference;
- typedef CharT const &const_reference;
-
- typedef const_pointer iterator;
- typedef const_pointer const_iterator;
- typedef std::reverse_iterator<const_iterator> reverse_iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-
- typedef std::size_t size_type;
- typedef std::ptrdiff_t difference_type;
-
- // 24.4.2.1 Construction and assignment:
-
- nssv_constexpr basic_string_view() nssv_noexcept : data_(nssv_nullptr), size_(0) {}
-
-#if nssv_CPP11_OR_GREATER
- nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept = default;
-#else
- nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept : data_(other.data_),
- size_(other.size_) {}
-#endif
-
- nssv_constexpr basic_string_view(CharT const *s, size_type count) nssv_noexcept // non-standard noexcept
- : data_(s),
- size_(count) {}
-
- nssv_constexpr basic_string_view(CharT const *s) nssv_noexcept // non-standard noexcept
- : data_(s)
-#if nssv_CPP17_OR_GREATER
- ,
- size_(Traits::length(s))
-#elif nssv_CPP11_OR_GREATER
- ,
- size_(detail::length(s))
-#else
- ,
- size_(Traits::length(s))
-#endif
- {
- }
-
- // Assignment:
-
-#if nssv_CPP11_OR_GREATER
- nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept = default;
-#else
- nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept {
- data_ = other.data_;
- size_ = other.size_;
- return *this;
- }
-#endif
-
- // 24.4.2.2 Iterator support:
-
- nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; }
- nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; }
-
- nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
- nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); }
-
- nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator(end()); }
- nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator(begin()); }
-
- nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
- nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); }
-
- // 24.4.2.3 Capacity:
-
- nssv_constexpr size_type size() const nssv_noexcept { return size_; }
- nssv_constexpr size_type length() const nssv_noexcept { return size_; }
- nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits<size_type>::max)(); }
-
- // since C++20
- nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept { return 0 == size_; }
-
- // 24.4.2.4 Element access:
-
- nssv_constexpr const_reference operator[](size_type pos) const { return data_at(pos); }
-
- nssv_constexpr14 const_reference at(size_type pos) const {
-#if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos < size());
-#else
- if (pos >= size()) {
- throw std::out_of_range("nonstd::string_view::at()");
- }
-#endif
- return data_at(pos);
- }
-
- nssv_constexpr const_reference front() const { return data_at(0); }
- nssv_constexpr const_reference back() const { return data_at(size() - 1); }
-
- nssv_constexpr const_pointer data() const nssv_noexcept { return data_; }
-
- // 24.4.2.5 Modifiers:
-
- nssv_constexpr14 void remove_prefix(size_type n) {
- assert(n <= size());
- data_ += n;
- size_ -= n;
- }
-
- nssv_constexpr14 void remove_suffix(size_type n) {
- assert(n <= size());
- size_ -= n;
- }
-
- nssv_constexpr14 void swap(basic_string_view &other) nssv_noexcept {
- using std::swap;
- swap(data_, other.data_);
- swap(size_, other.size_);
- }
-
- // 24.4.2.6 String operations:
-
- size_type copy(CharT *dest, size_type n, size_type pos = 0) const {
-#if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos <= size());
-#else
- if (pos > size()) {
- throw std::out_of_range("nonstd::string_view::copy()");
- }
-#endif
- const size_type rlen = (std::min)(n, size() - pos);
-
- (void)Traits::copy(dest, data() + pos, rlen);
-
- return rlen;
- }
-
- nssv_constexpr14 basic_string_view substr(size_type pos = 0, size_type n = npos) const {
-#if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos <= size());
-#else
- if (pos > size()) {
- throw std::out_of_range("nonstd::string_view::substr()");
- }
-#endif
- return basic_string_view(data() + pos, (std::min)(n, size() - pos));
- }
-
- // compare(), 6x:
-
- nssv_constexpr14 int compare(basic_string_view other) const nssv_noexcept // (1)
- {
- if (const int result = Traits::compare(data(), other.data(), (std::min)(size(), other.size()))) {
- return result;
- }
-
- return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other) const // (2)
- {
- return substr(pos1, n1).compare(other);
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other, size_type pos2,
- size_type n2) const // (3)
- {
- return substr(pos1, n1).compare(other.substr(pos2, n2));
- }
-
- nssv_constexpr int compare(CharT const *s) const // (4)
- {
- return compare(basic_string_view(s));
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s) const // (5)
- {
- return substr(pos1, n1).compare(basic_string_view(s));
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s, size_type n2) const // (6)
- {
- return substr(pos1, n1).compare(basic_string_view(s, n2));
- }
-
- // 24.4.2.7 Searching:
-
- // starts_with(), 3x, since C++20:
-
- nssv_constexpr bool starts_with(basic_string_view v) const nssv_noexcept // (1)
- {
- return size() >= v.size() && compare(0, v.size(), v) == 0;
- }
-
- nssv_constexpr bool starts_with(CharT c) const nssv_noexcept // (2)
- {
- return starts_with(basic_string_view(&c, 1));
- }
-
- nssv_constexpr bool starts_with(CharT const *s) const // (3)
- {
- return starts_with(basic_string_view(s));
- }
-
- // ends_with(), 3x, since C++20:
-
- nssv_constexpr bool ends_with(basic_string_view v) const nssv_noexcept // (1)
- {
- return size() >= v.size() && compare(size() - v.size(), npos, v) == 0;
- }
-
- nssv_constexpr bool ends_with(CharT c) const nssv_noexcept // (2)
- {
- return ends_with(basic_string_view(&c, 1));
- }
-
- nssv_constexpr bool ends_with(CharT const *s) const // (3)
- {
- return ends_with(basic_string_view(s));
- }
-
- // find(), 4x:
-
- nssv_constexpr14 size_type find(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return assert(v.size() == 0 || v.data() != nssv_nullptr),
- pos >= size() ? npos : to_pos(std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
- }
-
- nssv_constexpr14 size_type find(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr14 size_type find(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return find(basic_string_view(s, n), pos);
- }
-
- nssv_constexpr14 size_type find(CharT const *s, size_type pos = 0) const // (4)
- {
- return find(basic_string_view(s), pos);
- }
-
- // rfind(), 4x:
-
- nssv_constexpr14 size_type rfind(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- if (size() < v.size()) {
- return npos;
- }
-
- if (v.empty()) {
- return (std::min)(size(), pos);
- }
-
- const_iterator last = cbegin() + (std::min)(size() - v.size(), pos) + v.size();
- const_iterator result = std::find_end(cbegin(), last, v.cbegin(), v.cend(), Traits::eq);
-
- return result != last ? size_type(result - cbegin()) : npos;
- }
-
- nssv_constexpr14 size_type rfind(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return rfind(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr14 size_type rfind(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return rfind(basic_string_view(s, n), pos);
- }
-
- nssv_constexpr14 size_type rfind(CharT const *s, size_type pos = npos) const // (4)
- {
- return rfind(basic_string_view(s), pos);
- }
-
- // find_first_of(), 4x:
-
- nssv_constexpr size_type find_first_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return pos >= size() ? npos
- : to_pos(std::find_first_of(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
- }
-
- nssv_constexpr size_type find_first_of(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find_first_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_first_of(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return find_first_of(basic_string_view(s, n), pos);
- }
-
- nssv_constexpr size_type find_first_of(CharT const *s, size_type pos = 0) const // (4)
- {
- return find_first_of(basic_string_view(s), pos);
- }
-
- // find_last_of(), 4x:
-
- nssv_constexpr size_type find_last_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- return empty() ? npos
- : pos >= size() ? find_last_of(v, size() - 1)
- : to_pos(std::find_first_of(const_reverse_iterator(cbegin() + pos + 1), crend(),
- v.cbegin(), v.cend(), Traits::eq));
- }
-
- nssv_constexpr size_type find_last_of(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return find_last_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_last_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_last_of(basic_string_view(s, count), pos);
- }
-
- nssv_constexpr size_type find_last_of(CharT const *s, size_type pos = npos) const // (4)
- {
- return find_last_of(basic_string_view(s), pos);
- }
-
- // find_first_not_of(), 4x:
-
- nssv_constexpr size_type find_first_not_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return pos >= size() ? npos : to_pos(std::find_if(cbegin() + pos, cend(), not_in_view(v)));
- }
-
- nssv_constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find_first_not_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_first_not_of(basic_string_view(s, count), pos);
- }
-
- nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos = 0) const // (4)
- {
- return find_first_not_of(basic_string_view(s), pos);
- }
-
- // find_last_not_of(), 4x:
-
- nssv_constexpr size_type find_last_not_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- return empty() ? npos
- : pos >= size()
- ? find_last_not_of(v, size() - 1)
- : to_pos(std::find_if(const_reverse_iterator(cbegin() + pos + 1), crend(), not_in_view(v)));
- }
-
- nssv_constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return find_last_not_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_last_not_of(basic_string_view(s, count), pos);
- }
-
- nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos = npos) const // (4)
- {
- return find_last_not_of(basic_string_view(s), pos);
- }
-
- // Constants:
-
-#if nssv_CPP17_OR_GREATER
- static nssv_constexpr size_type npos = size_type(-1);
-#elif nssv_CPP11_OR_GREATER
- enum : size_type { npos = size_type(-1) };
-#else
- enum { npos = size_type(-1) };
-#endif
-
- private:
- struct not_in_view {
- const basic_string_view v;
-
- nssv_constexpr explicit not_in_view(basic_string_view v) : v(v) {}
-
- nssv_constexpr bool operator()(CharT c) const { return npos == v.find_first_of(c); }
- };
-
- nssv_constexpr size_type to_pos(const_iterator it) const { return it == cend() ? npos : size_type(it - cbegin()); }
-
- nssv_constexpr size_type to_pos(const_reverse_iterator it) const {
- return it == crend() ? npos : size_type(crend() - it - 1);
- }
-
- nssv_constexpr const_reference data_at(size_type pos) const {
-#if nssv_BETWEEN(nssv_COMPILER_GNUC_VERSION, 1, 500)
- return data_[pos];
-#else
- return assert(pos < size()), data_[pos];
-#endif
- }
-
- private:
- const_pointer data_;
- size_type size_;
-
- public:
-#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-
- template <class Allocator>
- basic_string_view(std::basic_string<CharT, Traits, Allocator> const &s) nssv_noexcept : data_(s.data()),
- size_(s.size()) {}
-
-#if nssv_HAVE_EXPLICIT_CONVERSION
-
- template <class Allocator> explicit operator std::basic_string<CharT, Traits, Allocator>() const {
- return to_string(Allocator());
- }
-
-#endif // nssv_HAVE_EXPLICIT_CONVERSION
-
-#if nssv_CPP11_OR_GREATER
-
- template <class Allocator = std::allocator<CharT>>
- std::basic_string<CharT, Traits, Allocator> to_string(Allocator const &a = Allocator()) const {
- return std::basic_string<CharT, Traits, Allocator>(begin(), end(), a);
- }
-
-#else
-
- std::basic_string<CharT, Traits> to_string() const { return std::basic_string<CharT, Traits>(begin(), end()); }
-
- template <class Allocator> std::basic_string<CharT, Traits, Allocator> to_string(Allocator const &a) const {
- return std::basic_string<CharT, Traits, Allocator>(begin(), end(), a);
- }
-
-#endif // nssv_CPP11_OR_GREATER
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
- };
-
- //
- // Non-member functions:
- //
-
- // 24.4.3 Non-member comparison functions:
- // lexicographically compare two string views (function template):
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
- // Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
- // Implementations shall provide sufficient additional overloads marked
- // constexpr and noexcept so that an object t with an implicit conversion
- // to S can be compared according to Table 67.
-
-#if !nssv_CPP11_OR_GREATER || nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 100, 141)
-
- // accomodate for older compilers:
-
- // ==
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) == 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
-
- // !=
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) != 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() != rhs.size() && lhs.compare(rhs) != 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return lhs.size() != rhs.size() || rhs.compare(lhs) != 0;
- }
-
- // <
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) > 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) > 0;
- }
-
- // <=
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) >= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) >= 0;
- }
-
- // >
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) < 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) < 0;
- }
-
- // >=
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) <= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) <= 0;
- }
-
-#else // newer compilers:
-
-#define nssv_BASIC_STRING_VIEW_I(T, U) typename std::decay<basic_string_view<T, U>>::type
-
-#if nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 140, 150)
-#define nssv_MSVC_ORDER(x) , int = x
-#else
-#define nssv_MSVC_ORDER(x) /*, int=x*/
-#endif
-
- // ==
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator==(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
-
- // !=
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.size() != rhs.size() || lhs.compare(rhs) != 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator!=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
-
- // <
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator<(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
-
- // <=
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator<=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
-
- // >
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator>(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
-
- // >=
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator>=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
-
-#undef nssv_MSVC_ORDER
-#undef nssv_BASIC_STRING_VIEW_I
-
-#endif // compiler-dependent approach to comparisons
-
- // 24.4.4 Inserters and extractors:
-
- namespace detail {
-
- template <class Stream> void write_padding(Stream &os, std::streamsize n) {
- for (std::streamsize i = 0; i < n; ++i)
- os.rdbuf()->sputc(os.fill());
- }
-
- template <class Stream, class View> Stream &write_to_stream(Stream &os, View const &sv) {
- typename Stream::sentry sentry(os);
-
- if (!os)
- return os;
-
- const std::streamsize length = static_cast<std::streamsize>(sv.length());
-
- // Whether, and how, to pad:
- const bool pad = (length < os.width());
- const bool left_pad = pad && (os.flags() & std::ios_base::adjustfield) == std::ios_base::right;
-
- if (left_pad)
- write_padding(os, os.width() - length);
-
- // Write span characters:
- os.rdbuf()->sputn(sv.begin(), length);
-
- if (pad && !left_pad)
- write_padding(os, os.width() - length);
-
- // Reset output stream width:
- os.width(0);
-
- return os;
- }
-
- } // namespace detail
-
- template <class CharT, class Traits>
- std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,
- basic_string_view<CharT, Traits> sv) {
- return detail::write_to_stream(os, sv);
- }
-
- // Several typedefs for common character types are provided:
-
- typedef basic_string_view<char> string_view;
- typedef basic_string_view<wchar_t> wstring_view;
-#if nssv_HAVE_WCHAR16_T
- typedef basic_string_view<char16_t> u16string_view;
- typedef basic_string_view<char32_t> u32string_view;
-#endif
-
- } // namespace sv_lite
-} // namespace nonstd::sv_lite
-
-//
-// 24.4.6 Suffix for basic_string_view literals:
-//
-
-#if nssv_HAVE_USER_DEFINED_LITERALS
-
-namespace nonstd {
-nssv_inline_ns namespace literals {
- nssv_inline_ns namespace string_view_literals {
-
-#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
-
- nssv_constexpr nonstd::sv_lite::string_view operator"" sv(const char *str, size_t len) nssv_noexcept // (1)
- {
- return nonstd::sv_lite::string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::u16string_view operator"" sv(const char16_t *str, size_t len) nssv_noexcept // (2)
- {
- return nonstd::sv_lite::u16string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::u32string_view operator"" sv(const char32_t *str, size_t len) nssv_noexcept // (3)
- {
- return nonstd::sv_lite::u32string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::wstring_view operator"" sv(const wchar_t *str, size_t len) nssv_noexcept // (4)
- {
- return nonstd::sv_lite::wstring_view {str, len};
- }
-
-#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-
- nssv_constexpr nonstd::sv_lite::string_view operator"" _sv(const char *str, size_t len) nssv_noexcept // (1)
- {
- return nonstd::sv_lite::string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::u16string_view operator"" _sv(const char16_t *str, size_t len) nssv_noexcept // (2)
- {
- return nonstd::sv_lite::u16string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::u32string_view operator"" _sv(const char32_t *str, size_t len) nssv_noexcept // (3)
- {
- return nonstd::sv_lite::u32string_view {str, len};
- }
-
- nssv_constexpr nonstd::sv_lite::wstring_view operator"" _sv(const wchar_t *str, size_t len) nssv_noexcept // (4)
- {
- return nonstd::sv_lite::wstring_view {str, len};
- }
-
-#endif // nssv_CONFIG_USR_SV_OPERATOR
- }
-}
-} // namespace nonstd
-
-#endif
-
-//
-// Extensions for std::string:
-//
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-namespace sv_lite {
-
-// Exclude MSVC 14 (19.00): it yields ambiguous to_string():
-
-#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
-
-template <class CharT, class Traits, class Allocator = std::allocator<CharT>>
-std::basic_string<CharT, Traits, Allocator> to_string(basic_string_view<CharT, Traits> v,
- Allocator const &a = Allocator()) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
-}
-
-#else
-
-template <class CharT, class Traits> std::basic_string<CharT, Traits> to_string(basic_string_view<CharT, Traits> v) {
- return std::basic_string<CharT, Traits>(v.begin(), v.end());
-}
-
-template <class CharT, class Traits, class Allocator>
-std::basic_string<CharT, Traits, Allocator> to_string(basic_string_view<CharT, Traits> v, Allocator const &a) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
-}
-
-#endif // nssv_CPP11_OR_GREATER
-
-template <class CharT, class Traits, class Allocator>
-basic_string_view<CharT, Traits> to_string_view(std::basic_string<CharT, Traits, Allocator> const &s) {
- return basic_string_view<CharT, Traits>(s.data(), s.size());
-}
-
-} // namespace sv_lite
-} // namespace nonstd
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-//
-// make types and algorithms available in namespace nonstd:
-//
-
-namespace nonstd {
-
-using sv_lite::basic_string_view;
-using sv_lite::string_view;
-using sv_lite::wstring_view;
-
-#if nssv_HAVE_WCHAR16_T
-using sv_lite::u16string_view;
-#endif
-#if nssv_HAVE_WCHAR32_T
-using sv_lite::u32string_view;
-#endif
-
-// literal "sv"
-
-using sv_lite::operator==;
-using sv_lite::operator!=;
-using sv_lite::operator<;
-using sv_lite::operator<=;
-using sv_lite::operator>;
-using sv_lite::operator>=;
-
-using sv_lite::operator<<;
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-using sv_lite::to_string;
-using sv_lite::to_string_view;
-#endif
-
-} // namespace nonstd
-
-// 24.4.5 Hash support (C++11):
-
-// Note: The hash value of a string view object is equal to the hash value of
-// the corresponding string object.
-
-#if nssv_HAVE_STD_HASH
-
-#include <functional>
-
-namespace std {
-
-template <> struct hash<nonstd::string_view> {
-public:
- std::size_t operator()(nonstd::string_view v) const nssv_noexcept {
- return std::hash<std::string>()(std::string(v.data(), v.size()));
- }
-};
-
-template <> struct hash<nonstd::wstring_view> {
-public:
- std::size_t operator()(nonstd::wstring_view v) const nssv_noexcept {
- return std::hash<std::wstring>()(std::wstring(v.data(), v.size()));
- }
-};
-
-template <> struct hash<nonstd::u16string_view> {
-public:
- std::size_t operator()(nonstd::u16string_view v) const nssv_noexcept {
- return std::hash<std::u16string>()(std::u16string(v.data(), v.size()));
- }
-};
-
-template <> struct hash<nonstd::u32string_view> {
-public:
- std::size_t operator()(nonstd::u32string_view v) const nssv_noexcept {
- return std::hash<std::u32string>()(std::u32string(v.data(), v.size()));
- }
-};
-
-} // namespace std
-
-#endif // nssv_HAVE_STD_HASH
-
-nssv_RESTORE_WARNINGS()
-
-#endif // nssv_HAVE_STD_STRING_VIEW
-#endif // NONSTD_SV_LITE_H_INCLUDED
diff --git a/include/inja/template.hpp b/include/inja/template.hpp
index 9de0a96..a396dd5 100644
--- a/include/inja/template.hpp
+++ b/include/inja/template.hpp
@@ -1,5 +1,3 @@
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_TEMPLATE_HPP_
#define INCLUDE_INJA_TEMPLATE_HPP_
@@ -11,7 +9,6 @@
#include "node.hpp"
#include "statistics.hpp"
-
namespace inja {
/*!
@@ -20,9 +17,10 @@ namespace inja {
struct Template {
BlockNode root;
std::string content;
+ std::map<std::string, std::shared_ptr<BlockStatementNode>> block_storage;
- explicit Template() { }
- explicit Template(const std::string& content): content(content) { }
+ explicit Template() {}
+ explicit Template(const std::string& content): content(content) {}
/// Return number of variables (total number, not distinct ones) in the template
int count_variables() {
diff --git a/include/inja/token.hpp b/include/inja/token.hpp
index c000138..207e78e 100644
--- a/include/inja/token.hpp
+++ b/include/inja/token.hpp
@@ -1,11 +1,8 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_TOKEN_HPP_
#define INCLUDE_INJA_TOKEN_HPP_
#include <string>
-
-#include "string_view.hpp"
+#include <string_view>
namespace inja {
@@ -50,12 +47,12 @@ struct Token {
Unknown,
Eof,
};
-
+
Kind kind {Kind::Unknown};
- nonstd::string_view text;
+ std::string_view text;
explicit constexpr Token() = default;
- explicit constexpr Token(Kind kind, nonstd::string_view text) : kind(kind), text(text) {}
+ explicit constexpr Token(Kind kind, std::string_view text): kind(kind), text(text) {}
std::string describe() const {
switch (kind) {
diff --git a/include/inja/utils.hpp b/include/inja/utils.hpp
index fb1736c..4de05fd 100644
--- a/include/inja/utils.hpp
+++ b/include/inja/utils.hpp
@@ -1,53 +1,42 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_UTILS_HPP_
#define INCLUDE_INJA_UTILS_HPP_
#include <algorithm>
#include <fstream>
#include <string>
+#include <string_view>
#include <utility>
#include "exceptions.hpp"
-#include "string_view.hpp"
namespace inja {
-inline void open_file_or_throw(const std::string &path, std::ifstream &file) {
- file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
- try {
- file.open(path);
- } catch (const std::ios_base::failure & /*e*/) {
- INJA_THROW(FileError("failed accessing file at '" + path + "'"));
- }
-}
-
namespace string_view {
-inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
+inline std::string_view slice(std::string_view view, size_t start, size_t end) {
start = std::min(start, view.size());
end = std::min(std::max(start, end), view.size());
return view.substr(start, end - start);
}
-inline std::pair<nonstd::string_view, nonstd::string_view> split(nonstd::string_view view, char Separator) {
+inline std::pair<std::string_view, std::string_view> split(std::string_view view, char Separator) {
size_t idx = view.find(Separator);
- if (idx == nonstd::string_view::npos) {
- return std::make_pair(view, nonstd::string_view());
+ if (idx == std::string_view::npos) {
+ return std::make_pair(view, std::string_view());
}
- return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, nonstd::string_view::npos));
+ return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, std::string_view::npos));
}
-inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) {
+inline bool starts_with(std::string_view view, std::string_view prefix) {
return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
}
} // namespace string_view
-inline SourceLocation get_source_location(nonstd::string_view content, size_t pos) {
+inline SourceLocation get_source_location(std::string_view content, size_t pos) {
// Get line and offset position (starts at 1:1)
auto sliced = string_view::slice(content, 0, pos);
std::size_t last_newline = sliced.rfind("\n");
- if (last_newline == nonstd::string_view::npos) {
+ if (last_newline == std::string_view::npos) {
return {1, sliced.length() + 1};
}
@@ -65,6 +54,17 @@ inline SourceLocation get_source_location(nonstd::string_view content, size_t po
return {count_lines + 1, sliced.length() - last_newline};
}
+inline void replace_substring(std::string& s, const std::string& f, const std::string& t) {
+ if (f.empty()) {
+ return;
+ }
+ for (auto pos = s.find(f); // find first occurrence of f
+ pos != std::string::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
+ {}
+}
+
} // namespace inja
#endif // INCLUDE_INJA_UTILS_HPP_
diff --git a/single_include/inja/inja.hpp b/single_include/inja/inja.hpp
index 42dea1c..6aad870 100644
--- a/single_include/inja/inja.hpp
+++ b/single_include/inja/inja.hpp
@@ -1,20 +1,57 @@
-// Copyright (c) 2020 Pantor. All rights reserved.
+/*
+ ___ _ Version 3.3
+ |_ _|_ __ (_) __ _ https://github.com/pantor/inja
+ | || '_ \ | |/ _` | Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+ | || | | || | (_| |
+ |___|_| |_|/ |\__,_| Copyright (c) 2018-2021 Lars Berscheid
+ |__/
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
#ifndef INCLUDE_INJA_INJA_HPP_
#define INCLUDE_INJA_INJA_HPP_
#include <nlohmann/json.hpp>
+namespace inja {
+#ifndef INJA_DATA_TYPE
+using json = nlohmann::json;
+#else
+using json = INJA_DATA_TYPE;
+#endif
+} // namespace inja
+
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION)
- #define INJA_THROW(exception) throw exception
+#ifndef INJA_THROW
+#define INJA_THROW(exception) throw exception
+#endif
#else
- #include <cstdlib>
- #define INJA_THROW(exception) std::abort()
+#include <cstdlib>
+#ifndef INJA_THROW
+#define INJA_THROW(exception) \
+ std::abort(); \
+ std::ignore = exception
+#endif
+#ifndef INJA_NOEXCEPTION
+#define INJA_NOEXCEPTION
+#endif
#endif
// #include "environment.hpp"
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
#define INCLUDE_INJA_ENVIRONMENT_HPP_
@@ -23,1435 +60,760 @@
#include <memory>
#include <sstream>
#include <string>
-
-#include <nlohmann/json.hpp>
+#include <string_view>
// #include "config.hpp"
-// Copyright (c) 2019 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_CONFIG_HPP_
#define INCLUDE_INJA_CONFIG_HPP_
#include <functional>
#include <string>
-// #include "string_view.hpp"
-// Copyright 2017-2019 by Martin Moene
-//
-// string-view lite, a C++17-like string_view for C++98 and later.
-// For more information see https://github.com/martinmoene/string-view-lite
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
-
-#ifndef NONSTD_SV_LITE_H_INCLUDED
-#define NONSTD_SV_LITE_H_INCLUDED
-
-#define string_view_lite_MAJOR 1
-#define string_view_lite_MINOR 4
-#define string_view_lite_PATCH 0
-
-#define string_view_lite_VERSION \
- nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY( \
- string_view_lite_PATCH)
-
-#define nssv_STRINGIFY(x) nssv_STRINGIFY_(x)
-#define nssv_STRINGIFY_(x) #x
-
-// string-view lite configuration:
-
-#define nssv_STRING_VIEW_DEFAULT 0
-#define nssv_STRING_VIEW_NONSTD 1
-#define nssv_STRING_VIEW_STD 2
-
-#if !defined(nssv_CONFIG_SELECT_STRING_VIEW)
-#define nssv_CONFIG_SELECT_STRING_VIEW (nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD)
-#endif
-
-#if defined(nssv_CONFIG_SELECT_STD_STRING_VIEW) || defined(nssv_CONFIG_SELECT_NONSTD_STRING_VIEW)
-#error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
-#endif
-
-#ifndef nssv_CONFIG_STD_SV_OPERATOR
-#define nssv_CONFIG_STD_SV_OPERATOR 0
-#endif
-
-#ifndef nssv_CONFIG_USR_SV_OPERATOR
-#define nssv_CONFIG_USR_SV_OPERATOR 1
-#endif
-
-#ifdef nssv_CONFIG_CONVERSION_STD_STRING
-#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
-#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
-#endif
-
-#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
-#define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
-#endif
-
-#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-#define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
-#endif
-
-// Control presence of exception handling (try and auto discover):
-
-#ifndef nssv_CONFIG_NO_EXCEPTIONS
-#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
-#define nssv_CONFIG_NO_EXCEPTIONS 0
-#else
-#define nssv_CONFIG_NO_EXCEPTIONS 1
-#endif
-#endif
-
-// C++ language version detection (C++20 is speculative):
-// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
-
-#ifndef nssv_CPLUSPLUS
-#if defined(_MSVC_LANG) && !defined(__clang__)
-#define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
-#else
-#define nssv_CPLUSPLUS __cplusplus
-#endif
-#endif
-
-#define nssv_CPP98_OR_GREATER (nssv_CPLUSPLUS >= 199711L)
-#define nssv_CPP11_OR_GREATER (nssv_CPLUSPLUS >= 201103L)
-#define nssv_CPP11_OR_GREATER_ (nssv_CPLUSPLUS >= 201103L)
-#define nssv_CPP14_OR_GREATER (nssv_CPLUSPLUS >= 201402L)
-#define nssv_CPP17_OR_GREATER (nssv_CPLUSPLUS >= 201703L)
-#define nssv_CPP20_OR_GREATER (nssv_CPLUSPLUS >= 202000L)
-
-// use C++17 std::string_view if available and requested:
-
-#if nssv_CPP17_OR_GREATER && defined(__has_include)
-#if __has_include(<string_view> )
-#define nssv_HAVE_STD_STRING_VIEW 1
-#else
-#define nssv_HAVE_STD_STRING_VIEW 0
-#endif
-#else
-#define nssv_HAVE_STD_STRING_VIEW 0
-#endif
-
-#define nssv_USES_STD_STRING_VIEW \
- ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || \
- ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW))
-
-#define nssv_HAVE_STARTS_WITH (nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW)
-#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
+// #include "template.hpp"
+#ifndef INCLUDE_INJA_TEMPLATE_HPP_
+#define INCLUDE_INJA_TEMPLATE_HPP_
-//
-// Use C++17 std::string_view:
-//
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
-#if nssv_USES_STD_STRING_VIEW
+// #include "node.hpp"
+#ifndef INCLUDE_INJA_NODE_HPP_
+#define INCLUDE_INJA_NODE_HPP_
+#include <string>
#include <string_view>
+#include <utility>
-// Extensions for std::string:
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-
-template <class CharT, class Traits, class Allocator = std::allocator<CharT>>
-std::basic_string<CharT, Traits, Allocator> to_string(std::basic_string_view<CharT, Traits> v,
- Allocator const &a = Allocator()) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
-}
-
-template <class CharT, class Traits, class Allocator>
-std::basic_string_view<CharT, Traits> to_string_view(std::basic_string<CharT, Traits, Allocator> const &s) {
- return std::basic_string_view<CharT, Traits>(s.data(), s.size());
-}
-
-// Literal operators sv and _sv:
-
-#if nssv_CONFIG_STD_SV_OPERATOR
-
-using namespace std::literals::string_view_literals;
-
-#endif
-
-#if nssv_CONFIG_USR_SV_OPERATOR
-
-inline namespace literals {
-inline namespace string_view_literals {
-
-constexpr std::string_view operator"" _sv(const char *str, size_t len) noexcept // (1)
-{
- return std::string_view {str, len};
-}
-
-constexpr std::u16string_view operator"" _sv(const char16_t *str, size_t len) noexcept // (2)
-{
- return std::u16string_view {str, len};
-}
-
-constexpr std::u32string_view operator"" _sv(const char32_t *str, size_t len) noexcept // (3)
-{
- return std::u32string_view {str, len};
-}
-
-constexpr std::wstring_view operator"" _sv(const wchar_t *str, size_t len) noexcept // (4)
-{
- return std::wstring_view {str, len};
-}
-
-} // namespace string_view_literals
-} // namespace literals
-
-#endif // nssv_CONFIG_USR_SV_OPERATOR
-
-} // namespace nonstd
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-
-using std::basic_string_view;
-using std::string_view;
-using std::u16string_view;
-using std::u32string_view;
-using std::wstring_view;
-
-// literal "sv" and "_sv", see above
-
-using std::operator==;
-using std::operator!=;
-using std::operator<;
-using std::operator<=;
-using std::operator>;
-using std::operator>=;
-
-using std::operator<<;
-
-} // namespace nonstd
-
-#else // nssv_HAVE_STD_STRING_VIEW
-
-//
-// Before C++17: use string_view lite:
-//
-
-// Compiler versions:
-//
-// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0)
-// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002)
-// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003)
-// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
-// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
-// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
-// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
-// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
-// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
-// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
-
-#if defined(_MSC_VER) && !defined(__clang__)
-#define nssv_COMPILER_MSVC_VER (_MSC_VER)
-#define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900)))
-#else
-#define nssv_COMPILER_MSVC_VER 0
-#define nssv_COMPILER_MSVC_VERSION 0
-#endif
-
-#define nssv_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch))
-
-#if defined(__clang__)
-#define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
-#else
-#define nssv_COMPILER_CLANG_VERSION 0
-#endif
-
-#if defined(__GNUC__) && !defined(__clang__)
-#define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
-#else
-#define nssv_COMPILER_GNUC_VERSION 0
-#endif
-
-// half-open range [lo..hi):
-#define nssv_BETWEEN(v, lo, hi) ((lo) <= (v) && (v) < (hi))
-
-// Presence of language and library features:
-
-#ifdef _HAS_CPP0X
-#define nssv_HAS_CPP0X _HAS_CPP0X
-#else
-#define nssv_HAS_CPP0X 0
-#endif
-
-// Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
-
-#if nssv_COMPILER_MSVC_VER >= 1900
-#undef nssv_CPP11_OR_GREATER
-#define nssv_CPP11_OR_GREATER 1
-#endif
-
-#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
-#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
-#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
-#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
-#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
-#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
-
-#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
-#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
-
-// Presence of C++11 language features:
-
-#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
-#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
-#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
-#define nssv_HAVE_NOEXCEPT nssv_CPP11_140
-#define nssv_HAVE_NULLPTR nssv_CPP11_100
-#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
-#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
-#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
-#define nssv_HAVE_WCHAR16_T nssv_CPP11_100
-#define nssv_HAVE_WCHAR32_T nssv_CPP11_100
-
-#if !((nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION) || nssv_BETWEEN(nssv_COMPILER_CLANG_VERSION, 300, 400))
-#define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
-#else
-#define nssv_HAVE_STD_DEFINED_LITERALS 0
-#endif
-
-// Presence of C++14 language features:
-
-#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
-
-// Presence of C++17 language features:
-
-#define nssv_HAVE_NODISCARD nssv_CPP17_000
-
-// Presence of C++ library features:
-
-#define nssv_HAVE_STD_HASH nssv_CPP11_120
-
-// C++ feature usage:
-
-#if nssv_HAVE_CONSTEXPR_11
-#define nssv_constexpr constexpr
-#else
-#define nssv_constexpr /*constexpr*/
-#endif
-
-#if nssv_HAVE_CONSTEXPR_14
-#define nssv_constexpr14 constexpr
-#else
-#define nssv_constexpr14 /*constexpr*/
-#endif
-
-#if nssv_HAVE_EXPLICIT_CONVERSION
-#define nssv_explicit explicit
-#else
-#define nssv_explicit /*explicit*/
-#endif
-
-#if nssv_HAVE_INLINE_NAMESPACE
-#define nssv_inline_ns inline
-#else
-#define nssv_inline_ns /*inline*/
-#endif
-
-#if nssv_HAVE_NOEXCEPT
-#define nssv_noexcept noexcept
-#else
-#define nssv_noexcept /*noexcept*/
-#endif
-
-//#if nssv_HAVE_REF_QUALIFIER
-//# define nssv_ref_qual &
-//# define nssv_refref_qual &&
-//#else
-//# define nssv_ref_qual /*&*/
-//# define nssv_refref_qual /*&&*/
-//#endif
-
-#if nssv_HAVE_NULLPTR
-#define nssv_nullptr nullptr
-#else
-#define nssv_nullptr NULL
-#endif
-
-#if nssv_HAVE_NODISCARD
-#define nssv_nodiscard [[nodiscard]]
-#else
-#define nssv_nodiscard /*[[nodiscard]]*/
-#endif
-
-// Additional includes:
-
-#include <algorithm>
-#include <cassert>
-#include <iterator>
-#include <limits>
-#include <ostream>
-#include <string> // std::char_traits<>
-
-#if !nssv_CONFIG_NO_EXCEPTIONS
-#include <stdexcept>
-#endif
-
-#if nssv_CPP11_OR_GREATER
-#include <type_traits>
-#endif
-
-// Clang, GNUC, MSVC warning suppression macros:
-
-#if defined(__clang__)
-#pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wuser-defined-literals"
-#elif defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wliteral-suffix"
-#endif // __clang__
-
-#if nssv_COMPILER_MSVC_VERSION >= 140
-#define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
-#define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress : code))
-#define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable : codes))
-#else
-#define nssv_SUPPRESS_MSGSL_WARNING(expr)
-#define nssv_SUPPRESS_MSVC_WARNING(code, descr)
-#define nssv_DISABLE_MSVC_WARNINGS(codes)
-#endif
+// #include "function_storage.hpp"
+#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
+#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
-#if defined(__clang__)
-#define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
-#elif defined(__GNUC__)
-#define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
-#elif nssv_COMPILER_MSVC_VERSION >= 140
-#define nssv_RESTORE_WARNINGS() __pragma(warning(pop))
-#else
-#define nssv_RESTORE_WARNINGS()
-#endif
+#include <string_view>
+#include <vector>
-// Suppress the following MSVC (GSL) warnings:
-// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not
-// start with an underscore are reserved
-// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
-// use brace initialization, gsl::narrow_cast or gsl::narow
-// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
+namespace inja {
-nssv_DISABLE_MSVC_WARNINGS(4455 26481 26472)
- // nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
- // nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
+using Arguments = std::vector<const json*>;
+using CallbackFunction = std::function<json(Arguments& args)>;
+using VoidCallbackFunction = std::function<void(Arguments& args)>;
- namespace nonstd {
- namespace sv_lite {
+/*!
+ * \brief Class for builtin functions and user-defined callbacks.
+ */
+class FunctionStorage {
+public:
+ enum class Operation {
+ Not,
+ And,
+ Or,
+ In,
+ Equal,
+ NotEqual,
+ Greater,
+ GreaterEqual,
+ Less,
+ LessEqual,
+ Add,
+ Subtract,
+ Multiplication,
+ Division,
+ Power,
+ Modulo,
+ AtId,
+ At,
+ Default,
+ DivisibleBy,
+ Even,
+ Exists,
+ ExistsInObject,
+ First,
+ Float,
+ Int,
+ IsArray,
+ IsBoolean,
+ IsFloat,
+ IsInteger,
+ IsNumber,
+ IsObject,
+ IsString,
+ Last,
+ Length,
+ Lower,
+ Max,
+ Min,
+ Odd,
+ Range,
+ Round,
+ Sort,
+ Upper,
+ Super,
+ Join,
+ Callback,
+ ParenLeft,
+ ParenRight,
+ None,
+ };
-#if nssv_CPP11_OR_GREATER
+ struct FunctionData {
+ explicit FunctionData(const Operation& op, const CallbackFunction& cb = CallbackFunction {}): operation(op), callback(cb) {}
+ const Operation operation;
+ const CallbackFunction callback;
+ };
- namespace detail {
+private:
+ const int VARIADIC {-1};
- // Expect tail call optimization to make length() non-recursive:
+ std::map<std::pair<std::string, int>, FunctionData> function_storage = {
+ {std::make_pair("at", 2), FunctionData {Operation::At}},
+ {std::make_pair("default", 2), FunctionData {Operation::Default}},
+ {std::make_pair("divisibleBy", 2), FunctionData {Operation::DivisibleBy}},
+ {std::make_pair("even", 1), FunctionData {Operation::Even}},
+ {std::make_pair("exists", 1), FunctionData {Operation::Exists}},
+ {std::make_pair("existsIn", 2), FunctionData {Operation::ExistsInObject}},
+ {std::make_pair("first", 1), FunctionData {Operation::First}},
+ {std::make_pair("float", 1), FunctionData {Operation::Float}},
+ {std::make_pair("int", 1), FunctionData {Operation::Int}},
+ {std::make_pair("isArray", 1), FunctionData {Operation::IsArray}},
+ {std::make_pair("isBoolean", 1), FunctionData {Operation::IsBoolean}},
+ {std::make_pair("isFloat", 1), FunctionData {Operation::IsFloat}},
+ {std::make_pair("isInteger", 1), FunctionData {Operation::IsInteger}},
+ {std::make_pair("isNumber", 1), FunctionData {Operation::IsNumber}},
+ {std::make_pair("isObject", 1), FunctionData {Operation::IsObject}},
+ {std::make_pair("isString", 1), FunctionData {Operation::IsString}},
+ {std::make_pair("last", 1), FunctionData {Operation::Last}},
+ {std::make_pair("length", 1), FunctionData {Operation::Length}},
+ {std::make_pair("lower", 1), FunctionData {Operation::Lower}},
+ {std::make_pair("max", 1), FunctionData {Operation::Max}},
+ {std::make_pair("min", 1), FunctionData {Operation::Min}},
+ {std::make_pair("odd", 1), FunctionData {Operation::Odd}},
+ {std::make_pair("range", 1), FunctionData {Operation::Range}},
+ {std::make_pair("round", 2), FunctionData {Operation::Round}},
+ {std::make_pair("sort", 1), FunctionData {Operation::Sort}},
+ {std::make_pair("upper", 1), FunctionData {Operation::Upper}},
+ {std::make_pair("super", 0), FunctionData {Operation::Super}},
+ {std::make_pair("super", 1), FunctionData {Operation::Super}},
+ {std::make_pair("join", 2), FunctionData {Operation::Join}},
+ };
- template <typename CharT> inline constexpr std::size_t length(CharT *s, std::size_t result = 0) {
- return *s == '\0' ? result : length(s + 1, result + 1);
+public:
+ void add_builtin(std::string_view name, int num_args, Operation op) {
+ function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {op});
}
- } // namespace detail
-
-#endif // nssv_CPP11_OR_GREATER
-
- template <class CharT, class Traits = std::char_traits<CharT>> class basic_string_view;
-
- //
- // basic_string_view:
- //
-
- template <class CharT, class Traits /* = std::char_traits<CharT> */
- >
- class basic_string_view {
- public:
- // Member types:
-
- typedef Traits traits_type;
- typedef CharT value_type;
-
- typedef CharT *pointer;
- typedef CharT const *const_pointer;
- typedef CharT &reference;
- typedef CharT const &const_reference;
-
- typedef const_pointer iterator;
- typedef const_pointer const_iterator;
- typedef std::reverse_iterator<const_iterator> reverse_iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-
- typedef std::size_t size_type;
- typedef std::ptrdiff_t difference_type;
-
- // 24.4.2.1 Construction and assignment:
-
- nssv_constexpr basic_string_view() nssv_noexcept : data_(nssv_nullptr), size_(0) {}
-
-#if nssv_CPP11_OR_GREATER
- nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept = default;
-#else
- nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept : data_(other.data_),
- size_(other.size_) {}
-#endif
-
- nssv_constexpr basic_string_view(CharT const *s, size_type count) nssv_noexcept // non-standard noexcept
- : data_(s),
- size_(count) {}
-
- nssv_constexpr basic_string_view(CharT const *s) nssv_noexcept // non-standard noexcept
- : data_(s)
-#if nssv_CPP17_OR_GREATER
- ,
- size_(Traits::length(s))
-#elif nssv_CPP11_OR_GREATER
- ,
- size_(detail::length(s))
-#else
- ,
- size_(Traits::length(s))
-#endif
- {
- }
-
- // Assignment:
-
-#if nssv_CPP11_OR_GREATER
- nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept = default;
-#else
- nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept {
- data_ = other.data_;
- size_ = other.size_;
- return *this;
- }
-#endif
-
- // 24.4.2.2 Iterator support:
-
- nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; }
- nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; }
-
- nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
- nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); }
-
- nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator(end()); }
- nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator(begin()); }
-
- nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
- nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); }
-
- // 24.4.2.3 Capacity:
-
- nssv_constexpr size_type size() const nssv_noexcept { return size_; }
- nssv_constexpr size_type length() const nssv_noexcept { return size_; }
- nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits<size_type>::max)(); }
-
- // since C++20
- nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept { return 0 == size_; }
-
- // 24.4.2.4 Element access:
-
- nssv_constexpr const_reference operator[](size_type pos) const { return data_at(pos); }
-
- nssv_constexpr14 const_reference at(size_type pos) const {
-#if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos < size());
-#else
- if (pos >= size()) {
- throw std::out_of_range("nonstd::string_view::at()");
- }
-#endif
- return data_at(pos);
- }
-
- nssv_constexpr const_reference front() const { return data_at(0); }
- nssv_constexpr const_reference back() const { return data_at(size() - 1); }
-
- nssv_constexpr const_pointer data() const nssv_noexcept { return data_; }
-
- // 24.4.2.5 Modifiers:
-
- nssv_constexpr14 void remove_prefix(size_type n) {
- assert(n <= size());
- data_ += n;
- size_ -= n;
- }
-
- nssv_constexpr14 void remove_suffix(size_type n) {
- assert(n <= size());
- size_ -= n;
- }
-
- nssv_constexpr14 void swap(basic_string_view &other) nssv_noexcept {
- using std::swap;
- swap(data_, other.data_);
- swap(size_, other.size_);
- }
-
- // 24.4.2.6 String operations:
-
- size_type copy(CharT *dest, size_type n, size_type pos = 0) const {
-#if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos <= size());
-#else
- if (pos > size()) {
- throw std::out_of_range("nonstd::string_view::copy()");
- }
-#endif
- const size_type rlen = (std::min)(n, size() - pos);
-
- (void)Traits::copy(dest, data() + pos, rlen);
-
- return rlen;
- }
-
- nssv_constexpr14 basic_string_view substr(size_type pos = 0, size_type n = npos) const {
-#if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos <= size());
-#else
- if (pos > size()) {
- throw std::out_of_range("nonstd::string_view::substr()");
- }
-#endif
- return basic_string_view(data() + pos, (std::min)(n, size() - pos));
- }
-
- // compare(), 6x:
-
- nssv_constexpr14 int compare(basic_string_view other) const nssv_noexcept // (1)
- {
- if (const int result = Traits::compare(data(), other.data(), (std::min)(size(), other.size()))) {
- return result;
- }
-
- return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other) const // (2)
- {
- return substr(pos1, n1).compare(other);
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other, size_type pos2,
- size_type n2) const // (3)
- {
- return substr(pos1, n1).compare(other.substr(pos2, n2));
- }
-
- nssv_constexpr int compare(CharT const *s) const // (4)
- {
- return compare(basic_string_view(s));
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s) const // (5)
- {
- return substr(pos1, n1).compare(basic_string_view(s));
- }
-
- nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s, size_type n2) const // (6)
- {
- return substr(pos1, n1).compare(basic_string_view(s, n2));
- }
-
- // 24.4.2.7 Searching:
-
- // starts_with(), 3x, since C++20:
-
- nssv_constexpr bool starts_with(basic_string_view v) const nssv_noexcept // (1)
- {
- return size() >= v.size() && compare(0, v.size(), v) == 0;
- }
-
- nssv_constexpr bool starts_with(CharT c) const nssv_noexcept // (2)
- {
- return starts_with(basic_string_view(&c, 1));
- }
-
- nssv_constexpr bool starts_with(CharT const *s) const // (3)
- {
- return starts_with(basic_string_view(s));
- }
-
- // ends_with(), 3x, since C++20:
-
- nssv_constexpr bool ends_with(basic_string_view v) const nssv_noexcept // (1)
- {
- return size() >= v.size() && compare(size() - v.size(), npos, v) == 0;
- }
-
- nssv_constexpr bool ends_with(CharT c) const nssv_noexcept // (2)
- {
- return ends_with(basic_string_view(&c, 1));
- }
-
- nssv_constexpr bool ends_with(CharT const *s) const // (3)
- {
- return ends_with(basic_string_view(s));
- }
-
- // find(), 4x:
-
- nssv_constexpr14 size_type find(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return assert(v.size() == 0 || v.data() != nssv_nullptr),
- pos >= size() ? npos : to_pos(std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
- }
-
- nssv_constexpr14 size_type find(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr14 size_type find(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return find(basic_string_view(s, n), pos);
- }
-
- nssv_constexpr14 size_type find(CharT const *s, size_type pos = 0) const // (4)
- {
- return find(basic_string_view(s), pos);
- }
-
- // rfind(), 4x:
+ void add_callback(std::string_view name, int num_args, const CallbackFunction& callback) {
+ function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {Operation::Callback, callback});
+ }
- nssv_constexpr14 size_type rfind(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- if (size() < v.size()) {
- return npos;
- }
+ FunctionData find_function(std::string_view name, int num_args) const {
+ auto it = function_storage.find(std::make_pair(static_cast<std::string>(name), num_args));
+ if (it != function_storage.end()) {
+ return it->second;
- if (v.empty()) {
- return (std::min)(size(), pos);
+ // Find variadic function
+ } else if (num_args > 0) {
+ it = function_storage.find(std::make_pair(static_cast<std::string>(name), VARIADIC));
+ if (it != function_storage.end()) {
+ return it->second;
}
-
- const_iterator last = cbegin() + (std::min)(size() - v.size(), pos) + v.size();
- const_iterator result = std::find_end(cbegin(), last, v.cbegin(), v.cend(), Traits::eq);
-
- return result != last ? size_type(result - cbegin()) : npos;
- }
-
- nssv_constexpr14 size_type rfind(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return rfind(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr14 size_type rfind(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return rfind(basic_string_view(s, n), pos);
- }
-
- nssv_constexpr14 size_type rfind(CharT const *s, size_type pos = npos) const // (4)
- {
- return rfind(basic_string_view(s), pos);
- }
-
- // find_first_of(), 4x:
-
- nssv_constexpr size_type find_first_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return pos >= size() ? npos
- : to_pos(std::find_first_of(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
- }
-
- nssv_constexpr size_type find_first_of(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find_first_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_first_of(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return find_first_of(basic_string_view(s, n), pos);
- }
-
- nssv_constexpr size_type find_first_of(CharT const *s, size_type pos = 0) const // (4)
- {
- return find_first_of(basic_string_view(s), pos);
- }
-
- // find_last_of(), 4x:
-
- nssv_constexpr size_type find_last_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- return empty() ? npos
- : pos >= size() ? find_last_of(v, size() - 1)
- : to_pos(std::find_first_of(const_reverse_iterator(cbegin() + pos + 1), crend(),
- v.cbegin(), v.cend(), Traits::eq));
- }
-
- nssv_constexpr size_type find_last_of(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return find_last_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_last_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_last_of(basic_string_view(s, count), pos);
- }
-
- nssv_constexpr size_type find_last_of(CharT const *s, size_type pos = npos) const // (4)
- {
- return find_last_of(basic_string_view(s), pos);
}
- // find_first_not_of(), 4x:
-
- nssv_constexpr size_type find_first_not_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return pos >= size() ? npos : to_pos(std::find_if(cbegin() + pos, cend(), not_in_view(v)));
- }
-
- nssv_constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find_first_not_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_first_not_of(basic_string_view(s, count), pos);
- }
-
- nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos = 0) const // (4)
- {
- return find_first_not_of(basic_string_view(s), pos);
- }
-
- // find_last_not_of(), 4x:
-
- nssv_constexpr size_type find_last_not_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- return empty() ? npos
- : pos >= size()
- ? find_last_not_of(v, size() - 1)
- : to_pos(std::find_if(const_reverse_iterator(cbegin() + pos + 1), crend(), not_in_view(v)));
- }
-
- nssv_constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return find_last_not_of(basic_string_view(&c, 1), pos);
- }
-
- nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_last_not_of(basic_string_view(s, count), pos);
- }
-
- nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos = npos) const // (4)
- {
- return find_last_not_of(basic_string_view(s), pos);
- }
-
- // Constants:
-
-#if nssv_CPP17_OR_GREATER
- static nssv_constexpr size_type npos = size_type(-1);
-#elif nssv_CPP11_OR_GREATER
- enum : size_type { npos = size_type(-1) };
-#else
- enum { npos = size_type(-1) };
-#endif
-
- private:
- struct not_in_view {
- const basic_string_view v;
-
- nssv_constexpr explicit not_in_view(basic_string_view v) : v(v) {}
+ return FunctionData {Operation::None};
+ }
+};
- nssv_constexpr bool operator()(CharT c) const { return npos == v.find_first_of(c); }
- };
+} // namespace inja
- nssv_constexpr size_type to_pos(const_iterator it) const { return it == cend() ? npos : size_type(it - cbegin()); }
+#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_
- nssv_constexpr size_type to_pos(const_reverse_iterator it) const {
- return it == crend() ? npos : size_type(crend() - it - 1);
- }
+// #include "utils.hpp"
+#ifndef INCLUDE_INJA_UTILS_HPP_
+#define INCLUDE_INJA_UTILS_HPP_
- nssv_constexpr const_reference data_at(size_type pos) const {
-#if nssv_BETWEEN(nssv_COMPILER_GNUC_VERSION, 1, 500)
- return data_[pos];
-#else
- return assert(pos < size()), data_[pos];
-#endif
- }
+#include <algorithm>
+#include <fstream>
+#include <string>
+#include <string_view>
+#include <utility>
- private:
- const_pointer data_;
- size_type size_;
+// #include "exceptions.hpp"
+#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
+#define INCLUDE_INJA_EXCEPTIONS_HPP_
- public:
-#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+#include <stdexcept>
+#include <string>
- template <class Allocator>
- basic_string_view(std::basic_string<CharT, Traits, Allocator> const &s) nssv_noexcept : data_(s.data()),
- size_(s.size()) {}
+namespace inja {
-#if nssv_HAVE_EXPLICIT_CONVERSION
+struct SourceLocation {
+ size_t line;
+ size_t column;
+};
- template <class Allocator> explicit operator std::basic_string<CharT, Traits, Allocator>() const {
- return to_string(Allocator());
- }
+struct InjaError : public std::runtime_error {
+ const std::string type;
+ const std::string message;
-#endif // nssv_HAVE_EXPLICIT_CONVERSION
+ const SourceLocation location;
-#if nssv_CPP11_OR_GREATER
+ explicit InjaError(const std::string& type, const std::string& message)
+ : std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {}
- template <class Allocator = std::allocator<CharT>>
- std::basic_string<CharT, Traits, Allocator> to_string(Allocator const &a = Allocator()) const {
- return std::basic_string<CharT, Traits, Allocator>(begin(), end(), a);
- }
+ explicit InjaError(const std::string& type, const std::string& message, SourceLocation location)
+ : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" + std::to_string(location.column) + ") " + message),
+ type(type), message(message), location(location) {}
+};
-#else
+struct ParserError : public InjaError {
+ explicit ParserError(const std::string& message, SourceLocation location): InjaError("parser_error", message, location) {}
+};
- std::basic_string<CharT, Traits> to_string() const { return std::basic_string<CharT, Traits>(begin(), end()); }
+struct RenderError : public InjaError {
+ explicit RenderError(const std::string& message, SourceLocation location): InjaError("render_error", message, location) {}
+};
- template <class Allocator> std::basic_string<CharT, Traits, Allocator> to_string(Allocator const &a) const {
- return std::basic_string<CharT, Traits, Allocator>(begin(), end(), a);
- }
+struct FileError : public InjaError {
+ explicit FileError(const std::string& message): InjaError("file_error", message) {}
+ explicit FileError(const std::string& message, SourceLocation location): InjaError("file_error", message, location) {}
+};
-#endif // nssv_CPP11_OR_GREATER
+struct DataError : public InjaError {
+ explicit DataError(const std::string& message, SourceLocation location): InjaError("data_error", message, location) {}
+};
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
- };
+} // namespace inja
- //
- // Non-member functions:
- //
+#endif // INCLUDE_INJA_EXCEPTIONS_HPP_
- // 24.4.3 Non-member comparison functions:
- // lexicographically compare two string views (function template):
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
+namespace inja {
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
+namespace string_view {
+inline std::string_view slice(std::string_view view, size_t start, size_t end) {
+ start = std::min(start, view.size());
+ end = std::min(std::max(start, end), view.size());
+ return view.substr(start, end - start);
+}
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
+inline std::pair<std::string_view, std::string_view> split(std::string_view view, char Separator) {
+ size_t idx = view.find(Separator);
+ if (idx == std::string_view::npos) {
+ return std::make_pair(view, std::string_view());
}
+ return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, std::string_view::npos));
+}
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
+inline bool starts_with(std::string_view view, std::string_view prefix) {
+ return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
+}
+} // namespace string_view
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
+inline SourceLocation get_source_location(std::string_view content, size_t pos) {
+ // Get line and offset position (starts at 1:1)
+ auto sliced = string_view::slice(content, 0, pos);
+ std::size_t last_newline = sliced.rfind("\n");
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
+ if (last_newline == std::string_view::npos) {
+ return {1, sliced.length() + 1};
}
- // Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
- // Implementations shall provide sufficient additional overloads marked
- // constexpr and noexcept so that an object t with an implicit conversion
- // to S can be compared according to Table 67.
-
-#if !nssv_CPP11_OR_GREATER || nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 100, 141)
-
- // accomodate for older compilers:
-
- // ==
-
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
+ // Count newlines
+ size_t count_lines = 0;
+ size_t search_start = 0;
+ while (search_start <= sliced.size()) {
+ search_start = sliced.find("\n", search_start) + 1;
+ if (search_start == 0) {
+ break;
+ }
+ count_lines += 1;
}
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) == 0;
- }
+ return {count_lines + 1, sliced.length() - last_newline};
+}
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+inline void replace_substring(std::string& s, const std::string& f, const std::string& t) {
+ if (f.empty()) {
+ return;
}
+ for (auto pos = s.find(f); // find first occurrence of f
+ pos != std::string::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
+ {}
+}
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
+} // namespace inja
- // !=
+#endif // INCLUDE_INJA_UTILS_HPP_
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) != 0;
- }
+namespace inja {
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() != rhs.size() && lhs.compare(rhs) != 0;
- }
+class NodeVisitor;
+class BlockNode;
+class TextNode;
+class ExpressionNode;
+class LiteralNode;
+class DataNode;
+class FunctionNode;
+class ExpressionListNode;
+class StatementNode;
+class ForStatementNode;
+class ForArrayStatementNode;
+class ForObjectStatementNode;
+class IfStatementNode;
+class IncludeStatementNode;
+class ExtendsStatementNode;
+class BlockStatementNode;
+class SetStatementNode;
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return lhs.size() != rhs.size() || rhs.compare(lhs) != 0;
- }
+class NodeVisitor {
+public:
+ virtual ~NodeVisitor() = default;
- // <
+ virtual void visit(const BlockNode& node) = 0;
+ virtual void visit(const TextNode& node) = 0;
+ virtual void visit(const ExpressionNode& node) = 0;
+ virtual void visit(const LiteralNode& node) = 0;
+ virtual void visit(const DataNode& node) = 0;
+ virtual void visit(const FunctionNode& node) = 0;
+ virtual void visit(const ExpressionListNode& node) = 0;
+ virtual void visit(const StatementNode& node) = 0;
+ virtual void visit(const ForStatementNode& node) = 0;
+ virtual void visit(const ForArrayStatementNode& node) = 0;
+ virtual void visit(const ForObjectStatementNode& node) = 0;
+ virtual void visit(const IfStatementNode& node) = 0;
+ virtual void visit(const IncludeStatementNode& node) = 0;
+ virtual void visit(const ExtendsStatementNode& node) = 0;
+ virtual void visit(const BlockStatementNode& node) = 0;
+ virtual void visit(const SetStatementNode& node) = 0;
+};
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
+/*!
+ * \brief Base node class for the abstract syntax tree (AST).
+ */
+class AstNode {
+public:
+ virtual void accept(NodeVisitor& v) const = 0;
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) > 0;
- }
+ size_t pos;
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
+ AstNode(size_t pos): pos(pos) {}
+ virtual ~AstNode() {}
+};
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) > 0;
- }
+class BlockNode : public AstNode {
+public:
+ std::vector<std::shared_ptr<AstNode>> nodes;
- // <=
+ explicit BlockNode(): AstNode(0) {}
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) >= 0;
- }
+class TextNode : public AstNode {
+public:
+ const size_t length;
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
+ explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) {}
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) >= 0;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- // >
+class ExpressionNode : public AstNode {
+public:
+ explicit ExpressionNode(size_t pos): AstNode(pos) {}
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) < 0;
- }
+class LiteralNode : public ExpressionNode {
+public:
+ const json value;
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
+ explicit LiteralNode(std::string_view data_text, size_t pos): ExpressionNode(pos), value(json::parse(data_text)) {}
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) < 0;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- // >=
+class DataNode : public ExpressionNode {
+public:
+ const std::string name;
+ const json::json_pointer ptr;
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
+ static std::string convert_dot_to_ptr(std::string_view ptr_name) {
+ std::string result;
+ do {
+ std::string_view part;
+ std::tie(part, ptr_name) = string_view::split(ptr_name, '.');
+ result.push_back('/');
+ result.append(part.begin(), part.end());
+ } while (!ptr_name.empty());
+ return result;
}
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) <= 0;
- }
+ explicit DataNode(std::string_view ptr_name, size_t pos): ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {}
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) <= 0;
- }
+class FunctionNode : public ExpressionNode {
+ using Op = FunctionStorage::Operation;
-#else // newer compilers:
+public:
+ enum class Associativity {
+ Left,
+ Right,
+ };
-#define nssv_BASIC_STRING_VIEW_I(T, U) typename std::decay<basic_string_view<T, U>>::type
+ unsigned int precedence;
+ Associativity associativity;
-#if nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 140, 150)
-#define nssv_MSVC_ORDER(x) , int = x
-#else
-#define nssv_MSVC_ORDER(x) /*, int=x*/
-#endif
+ Op operation;
- // ==
+ std::string name;
+ int number_args; // Should also be negative -> -1 for unknown number
+ std::vector<std::shared_ptr<ExpressionNode>> arguments;
+ CallbackFunction callback;
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
+ explicit FunctionNode(std::string_view name, size_t pos)
+ : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) {}
+ explicit FunctionNode(Op operation, size_t pos): ExpressionNode(pos), operation(operation), number_args(1) {
+ switch (operation) {
+ case Op::Not: {
+ number_args = 1;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::And: {
+ number_args = 2;
+ precedence = 1;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Or: {
+ number_args = 2;
+ precedence = 1;
+ associativity = Associativity::Left;
+ } break;
+ case Op::In: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Equal: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::NotEqual: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Greater: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::GreaterEqual: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Less: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::LessEqual: {
+ number_args = 2;
+ precedence = 2;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Add: {
+ number_args = 2;
+ precedence = 3;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Subtract: {
+ number_args = 2;
+ precedence = 3;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Multiplication: {
+ number_args = 2;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Division: {
+ number_args = 2;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::Power: {
+ number_args = 2;
+ precedence = 5;
+ associativity = Associativity::Right;
+ } break;
+ case Op::Modulo: {
+ number_args = 2;
+ precedence = 4;
+ associativity = Associativity::Left;
+ } break;
+ case Op::AtId: {
+ number_args = 2;
+ precedence = 8;
+ associativity = Associativity::Left;
+ } break;
+ default: {
+ precedence = 1;
+ associativity = Associativity::Left;
+ }
+ }
}
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator==(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- // !=
+class ExpressionListNode : public AstNode {
+public:
+ std::shared_ptr<ExpressionNode> root;
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.size() != rhs.size() || lhs.compare(rhs) != 0;
- }
+ explicit ExpressionListNode(): AstNode(0) {}
+ explicit ExpressionListNode(size_t pos): AstNode(pos) {}
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator!=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- // <
-
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
+class StatementNode : public AstNode {
+public:
+ StatementNode(size_t pos): AstNode(pos) {}
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator<(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
+ virtual void accept(NodeVisitor& v) const = 0;
+};
- // <=
+class ForStatementNode : public StatementNode {
+public:
+ ExpressionListNode condition;
+ BlockNode body;
+ BlockNode* const parent;
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
+ ForStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent) {}
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator<=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
+ virtual void accept(NodeVisitor& v) const = 0;
+};
- // >
+class ForArrayStatementNode : public ForStatementNode {
+public:
+ const std::string value;
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
+ explicit ForArrayStatementNode(const std::string& value, BlockNode* const parent, size_t pos): ForStatementNode(parent, pos), value(value) {}
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator>(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- // >=
+class ForObjectStatementNode : public ForStatementNode {
+public:
+ const std::string key;
+ const std::string value;
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
+ explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode* const parent, size_t pos)
+ : ForStatementNode(parent, pos), key(key), value(value) {}
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator>=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
-#undef nssv_MSVC_ORDER
-#undef nssv_BASIC_STRING_VIEW_I
-
-#endif // compiler-dependent approach to comparisons
+class IfStatementNode : public StatementNode {
+public:
+ ExpressionListNode condition;
+ BlockNode true_statement;
+ BlockNode false_statement;
+ BlockNode* const parent;
- // 24.4.4 Inserters and extractors:
+ const bool is_nested;
+ bool has_false_statement {false};
- namespace detail {
+ explicit IfStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(false) {}
+ explicit IfStatementNode(bool is_nested, BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(is_nested) {}
- template <class Stream> void write_padding(Stream &os, std::streamsize n) {
- for (std::streamsize i = 0; i < n; ++i)
- os.rdbuf()->sputc(os.fill());
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- template <class Stream, class View> Stream &write_to_stream(Stream &os, View const &sv) {
- typename Stream::sentry sentry(os);
-
- if (!os)
- return os;
-
- const std::streamsize length = static_cast<std::streamsize>(sv.length());
-
- // Whether, and how, to pad:
- const bool pad = (length < os.width());
- const bool left_pad = pad && (os.flags() & std::ios_base::adjustfield) == std::ios_base::right;
-
- if (left_pad)
- write_padding(os, os.width() - length);
-
- // Write span characters:
- os.rdbuf()->sputn(sv.begin(), length);
-
- if (pad && !left_pad)
- write_padding(os, os.width() - length);
+class IncludeStatementNode : public StatementNode {
+public:
+ const std::string file;
- // Reset output stream width:
- os.width(0);
+ explicit IncludeStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
- return os;
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
}
+};
- } // namespace detail
+class ExtendsStatementNode : public StatementNode {
+public:
+ const std::string file;
- template <class CharT, class Traits>
- std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,
- basic_string_view<CharT, Traits> sv) {
- return detail::write_to_stream(os, sv);
- }
+ explicit ExtendsStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
- // Several typedefs for common character types are provided:
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
+ };
+};
- typedef basic_string_view<char> string_view;
- typedef basic_string_view<wchar_t> wstring_view;
-#if nssv_HAVE_WCHAR16_T
- typedef basic_string_view<char16_t> u16string_view;
- typedef basic_string_view<char32_t> u32string_view;
-#endif
+class BlockStatementNode : public StatementNode {
+public:
+ const std::string name;
+ BlockNode block;
+ BlockNode* const parent;
- } // namespace sv_lite
-} // namespace nonstd::sv_lite
+ explicit BlockStatementNode(BlockNode* const parent, const std::string& name, size_t pos): StatementNode(pos), name(name), parent(parent) {}
-//
-// 24.4.6 Suffix for basic_string_view literals:
-//
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
+ };
+};
-#if nssv_HAVE_USER_DEFINED_LITERALS
+class SetStatementNode : public StatementNode {
+public:
+ const std::string key;
+ ExpressionListNode expression;
-namespace nonstd {
-nssv_inline_ns namespace literals {
- nssv_inline_ns namespace string_view_literals {
+ explicit SetStatementNode(const std::string& key, size_t pos): StatementNode(pos), key(key) {}
-#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
+ void accept(NodeVisitor& v) const {
+ v.visit(*this);
+ }
+};
- nssv_constexpr nonstd::sv_lite::string_view operator"" sv(const char *str, size_t len) nssv_noexcept // (1)
- {
- return nonstd::sv_lite::string_view {str, len};
- }
+} // namespace inja
- nssv_constexpr nonstd::sv_lite::u16string_view operator"" sv(const char16_t *str, size_t len) nssv_noexcept // (2)
- {
- return nonstd::sv_lite::u16string_view {str, len};
- }
+#endif // INCLUDE_INJA_NODE_HPP_
- nssv_constexpr nonstd::sv_lite::u32string_view operator"" sv(const char32_t *str, size_t len) nssv_noexcept // (3)
- {
- return nonstd::sv_lite::u32string_view {str, len};
- }
+// #include "statistics.hpp"
+#ifndef INCLUDE_INJA_STATISTICS_HPP_
+#define INCLUDE_INJA_STATISTICS_HPP_
- nssv_constexpr nonstd::sv_lite::wstring_view operator"" sv(const wchar_t *str, size_t len) nssv_noexcept // (4)
- {
- return nonstd::sv_lite::wstring_view {str, len};
- }
+// #include "node.hpp"
-#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
-#if nssv_CONFIG_USR_SV_OPERATOR
+namespace inja {
- nssv_constexpr nonstd::sv_lite::string_view operator"" _sv(const char *str, size_t len) nssv_noexcept // (1)
- {
- return nonstd::sv_lite::string_view {str, len};
+/*!
+ * \brief A class for counting statistics on a Template.
+ */
+class StatisticsVisitor : public NodeVisitor {
+ void visit(const BlockNode& node) {
+ for (auto& n : node.nodes) {
+ n->accept(*this);
}
+ }
- nssv_constexpr nonstd::sv_lite::u16string_view operator"" _sv(const char16_t *str, size_t len) nssv_noexcept // (2)
- {
- return nonstd::sv_lite::u16string_view {str, len};
- }
+ void visit(const TextNode&) {}
+ void visit(const ExpressionNode&) {}
+ void visit(const LiteralNode&) {}
- nssv_constexpr nonstd::sv_lite::u32string_view operator"" _sv(const char32_t *str, size_t len) nssv_noexcept // (3)
- {
- return nonstd::sv_lite::u32string_view {str, len};
- }
+ void visit(const DataNode&) {
+ variable_counter += 1;
+ }
- nssv_constexpr nonstd::sv_lite::wstring_view operator"" _sv(const wchar_t *str, size_t len) nssv_noexcept // (4)
- {
- return nonstd::sv_lite::wstring_view {str, len};
+ void visit(const FunctionNode& node) {
+ for (auto& n : node.arguments) {
+ n->accept(*this);
}
-
-#endif // nssv_CONFIG_USR_SV_OPERATOR
}
-}
-} // namespace nonstd
-
-#endif
-
-//
-// Extensions for std::string:
-//
-
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-
-namespace nonstd {
-namespace sv_lite {
-
-// Exclude MSVC 14 (19.00): it yields ambiguous to_string():
-
-#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
-
-template <class CharT, class Traits, class Allocator = std::allocator<CharT>>
-std::basic_string<CharT, Traits, Allocator> to_string(basic_string_view<CharT, Traits> v,
- Allocator const &a = Allocator()) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
-}
-
-#else
-
-template <class CharT, class Traits> std::basic_string<CharT, Traits> to_string(basic_string_view<CharT, Traits> v) {
- return std::basic_string<CharT, Traits>(v.begin(), v.end());
-}
-
-template <class CharT, class Traits, class Allocator>
-std::basic_string<CharT, Traits, Allocator> to_string(basic_string_view<CharT, Traits> v, Allocator const &a) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
-}
-
-#endif // nssv_CPP11_OR_GREATER
-
-template <class CharT, class Traits, class Allocator>
-basic_string_view<CharT, Traits> to_string_view(std::basic_string<CharT, Traits, Allocator> const &s) {
- return basic_string_view<CharT, Traits>(s.data(), s.size());
-}
-
-} // namespace sv_lite
-} // namespace nonstd
-
-#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-//
-// make types and algorithms available in namespace nonstd:
-//
+ void visit(const ExpressionListNode& node) {
+ node.root->accept(*this);
+ }
-namespace nonstd {
+ void visit(const StatementNode&) {}
+ void visit(const ForStatementNode&) {}
-using sv_lite::basic_string_view;
-using sv_lite::string_view;
-using sv_lite::wstring_view;
+ void visit(const ForArrayStatementNode& node) {
+ node.condition.accept(*this);
+ node.body.accept(*this);
+ }
-#if nssv_HAVE_WCHAR16_T
-using sv_lite::u16string_view;
-#endif
-#if nssv_HAVE_WCHAR32_T
-using sv_lite::u32string_view;
-#endif
+ void visit(const ForObjectStatementNode& node) {
+ node.condition.accept(*this);
+ node.body.accept(*this);
+ }
-// literal "sv"
+ void visit(const IfStatementNode& node) {
+ node.condition.accept(*this);
+ node.true_statement.accept(*this);
+ node.false_statement.accept(*this);
+ }
-using sv_lite::operator==;
-using sv_lite::operator!=;
-using sv_lite::operator<;
-using sv_lite::operator<=;
-using sv_lite::operator>;
-using sv_lite::operator>=;
+ void visit(const IncludeStatementNode&) {}
-using sv_lite::operator<<;
+ void visit(const ExtendsStatementNode&) {}
-#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
-using sv_lite::to_string;
-using sv_lite::to_string_view;
-#endif
+ void visit(const BlockStatementNode& node) {
+ node.block.accept(*this);
+ }
-} // namespace nonstd
+ void visit(const SetStatementNode&) {}
-// 24.4.5 Hash support (C++11):
+public:
+ unsigned int variable_counter;
-// Note: The hash value of a string view object is equal to the hash value of
-// the corresponding string object.
+ explicit StatisticsVisitor(): variable_counter(0) {}
+};
-#if nssv_HAVE_STD_HASH
+} // namespace inja
-#include <functional>
+#endif // INCLUDE_INJA_STATISTICS_HPP_
-namespace std {
-template <> struct hash<nonstd::string_view> {
-public:
- std::size_t operator()(nonstd::string_view v) const nssv_noexcept {
- return std::hash<std::string>()(std::string(v.data(), v.size()));
- }
-};
+namespace inja {
-template <> struct hash<nonstd::wstring_view> {
-public:
- std::size_t operator()(nonstd::wstring_view v) const nssv_noexcept {
- return std::hash<std::wstring>()(std::wstring(v.data(), v.size()));
- }
-};
+/*!
+ * \brief The main inja Template.
+ */
+struct Template {
+ BlockNode root;
+ std::string content;
+ std::map<std::string, std::shared_ptr<BlockStatementNode>> block_storage;
-template <> struct hash<nonstd::u16string_view> {
-public:
- std::size_t operator()(nonstd::u16string_view v) const nssv_noexcept {
- return std::hash<std::u16string>()(std::u16string(v.data(), v.size()));
- }
-};
+ explicit Template() {}
+ explicit Template(const std::string& content): content(content) {}
-template <> struct hash<nonstd::u32string_view> {
-public:
- std::size_t operator()(nonstd::u32string_view v) const nssv_noexcept {
- return std::hash<std::u32string>()(std::u32string(v.data(), v.size()));
+ /// Return number of variables (total number, not distinct ones) in the template
+ int count_variables() {
+ auto statistic_visitor = StatisticsVisitor();
+ root.accept(statistic_visitor);
+ return statistic_visitor.variable_counter;
}
};
-} // namespace std
-
-#endif // nssv_HAVE_STD_HASH
+using TemplateStorage = std::map<std::string, Template>;
-nssv_RESTORE_WARNINGS()
+} // namespace inja
-#endif // nssv_HAVE_STD_STRING_VIEW
-#endif // NONSTD_SV_LITE_H_INCLUDED
+#endif // INCLUDE_INJA_TEMPLATE_HPP_
namespace inja {
@@ -1471,7 +833,9 @@ struct LexerConfig {
std::string expression_close {"}}"};
std::string expression_close_force_rstrip {"-}}"};
std::string comment_open {"{#"};
+ std::string comment_open_force_lstrip {"{#-"};
std::string comment_close {"#}"};
+ std::string comment_close_force_rstrip {"-#}"};
std::string open_chars {"#{"};
bool trim_blocks {false};
@@ -1500,6 +864,9 @@ struct LexerConfig {
if (open_chars.find(comment_open[0]) == std::string::npos) {
open_chars += comment_open[0];
}
+ if (open_chars.find(comment_open_force_lstrip[0]) == std::string::npos) {
+ open_chars += comment_open_force_lstrip[0];
+ }
}
};
@@ -1508,6 +875,8 @@ struct LexerConfig {
*/
struct ParserConfig {
bool search_included_templates_in_files {true};
+
+ std::function<Template(const std::string&, const std::string&)> include_callback;
};
/*!
@@ -1522,150 +891,8 @@ struct RenderConfig {
#endif // INCLUDE_INJA_CONFIG_HPP_
// #include "function_storage.hpp"
-// Copyright (c) 2020 Pantor. All rights reserved.
-
-#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
-#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
-
-#include <vector>
-
-// #include "string_view.hpp"
-
-
-namespace inja {
-
-using json = nlohmann::json;
-
-using Arguments = std::vector<const json *>;
-using CallbackFunction = std::function<json(Arguments &args)>;
-using VoidCallbackFunction = std::function<void(Arguments &args)>;
-
-/*!
- * \brief Class for builtin functions and user-defined callbacks.
- */
-class FunctionStorage {
-public:
- enum class Operation {
- Not,
- And,
- Or,
- In,
- Equal,
- NotEqual,
- Greater,
- GreaterEqual,
- Less,
- LessEqual,
- Add,
- Subtract,
- Multiplication,
- Division,
- Power,
- Modulo,
- AtId,
- At,
- Default,
- DivisibleBy,
- Even,
- Exists,
- ExistsInObject,
- First,
- Float,
- Int,
- IsArray,
- IsBoolean,
- IsFloat,
- IsInteger,
- IsNumber,
- IsObject,
- IsString,
- Last,
- Length,
- Lower,
- Max,
- Min,
- Odd,
- Range,
- Round,
- Sort,
- Upper,
- Callback,
- ParenLeft,
- ParenRight,
- None,
- };
-
- struct FunctionData {
- explicit FunctionData(const Operation &op, const CallbackFunction &cb = CallbackFunction{}) : operation(op), callback(cb) {}
- const Operation operation;
- const CallbackFunction callback;
- };
-
-private:
- const int VARIADIC {-1};
-
- std::map<std::pair<std::string, int>, FunctionData> function_storage = {
- {std::make_pair("at", 2), FunctionData { Operation::At }},
- {std::make_pair("default", 2), FunctionData { Operation::Default }},
- {std::make_pair("divisibleBy", 2), FunctionData { Operation::DivisibleBy }},
- {std::make_pair("even", 1), FunctionData { Operation::Even }},
- {std::make_pair("exists", 1), FunctionData { Operation::Exists }},
- {std::make_pair("existsIn", 2), FunctionData { Operation::ExistsInObject }},
- {std::make_pair("first", 1), FunctionData { Operation::First }},
- {std::make_pair("float", 1), FunctionData { Operation::Float }},
- {std::make_pair("int", 1), FunctionData { Operation::Int }},
- {std::make_pair("isArray", 1), FunctionData { Operation::IsArray }},
- {std::make_pair("isBoolean", 1), FunctionData { Operation::IsBoolean }},
- {std::make_pair("isFloat", 1), FunctionData { Operation::IsFloat }},
- {std::make_pair("isInteger", 1), FunctionData { Operation::IsInteger }},
- {std::make_pair("isNumber", 1), FunctionData { Operation::IsNumber }},
- {std::make_pair("isObject", 1), FunctionData { Operation::IsObject }},
- {std::make_pair("isString", 1), FunctionData { Operation::IsString }},
- {std::make_pair("last", 1), FunctionData { Operation::Last }},
- {std::make_pair("length", 1), FunctionData { Operation::Length }},
- {std::make_pair("lower", 1), FunctionData { Operation::Lower }},
- {std::make_pair("max", 1), FunctionData { Operation::Max }},
- {std::make_pair("min", 1), FunctionData { Operation::Min }},
- {std::make_pair("odd", 1), FunctionData { Operation::Odd }},
- {std::make_pair("range", 1), FunctionData { Operation::Range }},
- {std::make_pair("round", 2), FunctionData { Operation::Round }},
- {std::make_pair("sort", 1), FunctionData { Operation::Sort }},
- {std::make_pair("upper", 1), FunctionData { Operation::Upper }},
- };
-
-public:
- void add_builtin(nonstd::string_view name, int num_args, Operation op) {
- function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData { op });
- }
-
- void add_callback(nonstd::string_view name, int num_args, const CallbackFunction &callback) {
- function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData { Operation::Callback, callback });
- }
-
- FunctionData find_function(nonstd::string_view name, int num_args) const {
- auto it = function_storage.find(std::make_pair(static_cast<std::string>(name), num_args));
- if (it != function_storage.end()) {
- return it->second;
-
- // Find variadic function
- } else if (num_args > 0) {
- it = function_storage.find(std::make_pair(static_cast<std::string>(name), VARIADIC));
- if (it != function_storage.end()) {
- return it->second;
- }
- }
-
- return FunctionData { Operation::None };
- }
-};
-
-} // namespace inja
-
-#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_
// #include "parser.hpp"
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_PARSER_HPP_
#define INCLUDE_INJA_PARSER_HPP_
@@ -1673,68 +900,15 @@ public:
#include <stack>
#include <string>
#include <utility>
-#include <queue>
#include <vector>
// #include "config.hpp"
// #include "exceptions.hpp"
-// Copyright (c) 2020 Pantor. All rights reserved.
-
-#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
-#define INCLUDE_INJA_EXCEPTIONS_HPP_
-
-#include <stdexcept>
-#include <string>
-
-namespace inja {
-
-struct SourceLocation {
- size_t line;
- size_t column;
-};
-
-struct InjaError : public std::runtime_error {
- const std::string type;
- const std::string message;
-
- const SourceLocation location;
-
- explicit InjaError(const std::string &type, const std::string &message)
- : std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {}
-
- explicit InjaError(const std::string &type, const std::string &message, SourceLocation location)
- : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" +
- std::to_string(location.column) + ") " + message),
- type(type), message(message), location(location) {}
-};
-
-struct ParserError : public InjaError {
- explicit ParserError(const std::string &message, SourceLocation location) : InjaError("parser_error", message, location) {}
-};
-
-struct RenderError : public InjaError {
- explicit RenderError(const std::string &message, SourceLocation location) : InjaError("render_error", message, location) {}
-};
-
-struct FileError : public InjaError {
- explicit FileError(const std::string &message) : InjaError("file_error", message) {}
- explicit FileError(const std::string &message, SourceLocation location) : InjaError("file_error", message, location) {}
-};
-
-struct JsonError : public InjaError {
- explicit JsonError(const std::string &message, SourceLocation location) : InjaError("json_error", message, location) {}
-};
-
-} // namespace inja
-
-#endif // INCLUDE_INJA_EXCEPTIONS_HPP_
// #include "function_storage.hpp"
// #include "lexer.hpp"
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_LEXER_HPP_
#define INCLUDE_INJA_LEXER_HPP_
@@ -1744,15 +918,11 @@ struct JsonError : public InjaError {
// #include "config.hpp"
// #include "token.hpp"
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_TOKEN_HPP_
#define INCLUDE_INJA_TOKEN_HPP_
#include <string>
-
-// #include "string_view.hpp"
-
+#include <string_view>
namespace inja {
@@ -1797,12 +967,12 @@ struct Token {
Unknown,
Eof,
};
-
+
Kind kind {Kind::Unknown};
- nonstd::string_view text;
+ std::string_view text;
explicit constexpr Token() = default;
- explicit constexpr Token(Kind kind, nonstd::string_view text) : kind(kind), text(text) {}
+ explicit constexpr Token(Kind kind, std::string_view text): kind(kind), text(text) {}
std::string describe() const {
switch (kind) {
@@ -1823,78 +993,6 @@ struct Token {
#endif // INCLUDE_INJA_TOKEN_HPP_
// #include "utils.hpp"
-// Copyright (c) 2020 Pantor. All rights reserved.
-
-#ifndef INCLUDE_INJA_UTILS_HPP_
-#define INCLUDE_INJA_UTILS_HPP_
-
-#include <algorithm>
-#include <fstream>
-#include <string>
-#include <utility>
-
-// #include "exceptions.hpp"
-
-// #include "string_view.hpp"
-
-
-namespace inja {
-
-inline void open_file_or_throw(const std::string &path, std::ifstream &file) {
- file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
- try {
- file.open(path);
- } catch (const std::ios_base::failure & /*e*/) {
- INJA_THROW(FileError("failed accessing file at '" + path + "'"));
- }
-}
-
-namespace string_view {
-inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
- start = std::min(start, view.size());
- end = std::min(std::max(start, end), view.size());
- return view.substr(start, end - start);
-}
-
-inline std::pair<nonstd::string_view, nonstd::string_view> split(nonstd::string_view view, char Separator) {
- size_t idx = view.find(Separator);
- if (idx == nonstd::string_view::npos) {
- return std::make_pair(view, nonstd::string_view());
- }
- return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, nonstd::string_view::npos));
-}
-
-inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) {
- return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
-}
-} // namespace string_view
-
-inline SourceLocation get_source_location(nonstd::string_view content, size_t pos) {
- // Get line and offset position (starts at 1:1)
- auto sliced = string_view::slice(content, 0, pos);
- std::size_t last_newline = sliced.rfind("\n");
-
- if (last_newline == nonstd::string_view::npos) {
- return {1, sliced.length() + 1};
- }
-
- // Count newlines
- size_t count_lines = 0;
- size_t search_start = 0;
- while (search_start <= sliced.size()) {
- search_start = sliced.find("\n", search_start) + 1;
- if (search_start == 0) {
- break;
- }
- count_lines += 1;
- }
-
- return {count_lines + 1, sliced.length() - last_newline};
-}
-
-} // namespace inja
-
-#endif // INCLUDE_INJA_UTILS_HPP_
namespace inja {
@@ -1915,6 +1013,7 @@ class Lexer {
StatementStartForceLstrip,
StatementBody,
CommentStart,
+ CommentStartForceLstrip,
CommentBody,
};
@@ -1923,22 +1022,21 @@ class Lexer {
Number,
};
- const LexerConfig &config;
+ const LexerConfig& config;
State state;
MinusState minus_state;
- nonstd::string_view m_in;
+ std::string_view m_in;
size_t tok_start;
size_t pos;
-
- Token scan_body(nonstd::string_view close, Token::Kind closeKind, nonstd::string_view close_trim = nonstd::string_view(), bool trim = false) {
+ Token scan_body(std::string_view close, Token::Kind closeKind, std::string_view close_trim = std::string_view(), bool trim = false) {
again:
// skip whitespace (except for \n as it might be a close)
if (tok_start >= m_in.size()) {
return make_token(Token::Kind::Eof);
}
- char ch = m_in[tok_start];
+ const char ch = m_in[tok_start];
if (ch == ' ' || ch == '\t' || ch == '\r') {
tok_start += 1;
goto again;
@@ -1948,7 +1046,7 @@ class Lexer {
if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) {
state = State::Text;
pos = tok_start + close_trim.size();
- Token tok = make_token(closeKind);
+ const Token tok = make_token(closeKind);
skip_whitespaces_and_newlines();
return tok;
}
@@ -1956,7 +1054,7 @@ class Lexer {
if (inja::string_view::starts_with(m_in.substr(tok_start), close)) {
state = State::Text;
pos = tok_start + close.size();
- Token tok = make_token(closeKind);
+ const Token tok = make_token(closeKind);
if (trim) {
skip_whitespaces_and_first_newline();
}
@@ -1975,7 +1073,7 @@ class Lexer {
return scan_id();
}
- MinusState current_minus_state = minus_state;
+ const MinusState current_minus_state = minus_state;
if (minus_state == MinusState::Operator) {
minus_state = MinusState::Number;
}
@@ -2070,7 +1168,7 @@ class Lexer {
if (pos >= m_in.size()) {
break;
}
- char ch = m_in[pos];
+ const char ch = m_in[pos];
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
break;
}
@@ -2084,7 +1182,7 @@ class Lexer {
if (pos >= m_in.size()) {
break;
}
- char ch = m_in[pos];
+ const char ch = m_in[pos];
// be very permissive in lexer (we'll catch errors when conversion happens)
if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-') {
break;
@@ -2100,7 +1198,7 @@ class Lexer {
if (pos >= m_in.size()) {
break;
}
- char ch = m_in[pos++];
+ const char ch = m_in[pos++];
if (ch == '\\') {
escape = true;
} else if (!escape && ch == m_in[tok_start]) {
@@ -2112,7 +1210,9 @@ class Lexer {
return make_token(Token::Kind::String);
}
- Token make_token(Token::Kind kind) const { return Token(kind, string_view::slice(m_in, tok_start, pos)); }
+ Token make_token(Token::Kind kind) const {
+ return Token(kind, string_view::slice(m_in, tok_start, pos));
+ }
void skip_whitespaces_and_newlines() {
if (pos < m_in.size()) {
@@ -2130,7 +1230,7 @@ class Lexer {
}
if (pos < m_in.size()) {
- char ch = m_in[pos];
+ const char ch = m_in[pos];
if (ch == '\n') {
pos += 1;
} else if (ch == '\r') {
@@ -2142,10 +1242,10 @@ class Lexer {
}
}
- static nonstd::string_view clear_final_line_if_whitespace(nonstd::string_view text) {
- nonstd::string_view result = text;
+ static std::string_view clear_final_line_if_whitespace(std::string_view text) {
+ std::string_view result = text;
while (!result.empty()) {
- char ch = result.back();
+ const char ch = result.back();
if (ch == ' ' || ch == '\t') {
result.remove_suffix(1);
} else if (ch == '\n' || ch == '\r') {
@@ -2158,13 +1258,13 @@ class Lexer {
}
public:
- explicit Lexer(const LexerConfig &config) : config(config), state(State::Text), minus_state(MinusState::Number) {}
+ explicit Lexer(const LexerConfig& config): config(config), state(State::Text), minus_state(MinusState::Number) {}
SourceLocation current_position() const {
return get_source_location(m_in, tok_start);
}
- void start(nonstd::string_view input) {
+ void start(std::string_view input) {
m_in = input;
tok_start = 0;
pos = 0;
@@ -2189,8 +1289,8 @@ public:
default:
case State::Text: {
// fast-scan to first open character
- size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
- if (open_start == nonstd::string_view::npos) {
+ const size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
+ if (open_start == std::string_view::npos) {
// didn't find open, return remaining text as text token
pos = m_in.size();
return make_token(Token::Kind::Text);
@@ -2198,7 +1298,7 @@ public:
pos += open_start;
// try to match one of the opening sequences, and get the close
- nonstd::string_view open_str = m_in.substr(pos);
+ std::string_view open_str = m_in.substr(pos);
bool must_lstrip = false;
if (inja::string_view::starts_with(open_str, config.expression_open)) {
if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) {
@@ -2210,7 +1310,7 @@ public:
} else if (inja::string_view::starts_with(open_str, config.statement_open)) {
if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) {
state = State::StatementStartNoLstrip;
- } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip )) {
+ } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip)) {
state = State::StatementStartForceLstrip;
must_lstrip = true;
} else {
@@ -2218,8 +1318,13 @@ public:
must_lstrip = config.lstrip_blocks;
}
} else if (inja::string_view::starts_with(open_str, config.comment_open)) {
- state = State::CommentStart;
- must_lstrip = config.lstrip_blocks;
+ if (inja::string_view::starts_with(open_str, config.comment_open_force_lstrip)) {
+ state = State::CommentStartForceLstrip;
+ must_lstrip = true;
+ } else {
+ state = State::CommentStart;
+ must_lstrip = config.lstrip_blocks;
+ }
} else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) {
state = State::LineStart;
} else {
@@ -2227,7 +1332,7 @@ public:
goto again;
}
- nonstd::string_view text = string_view::slice(m_in, tok_start, pos);
+ std::string_view text = string_view::slice(m_in, tok_start, pos);
if (must_lstrip) {
text = clear_final_line_if_whitespace(text);
}
@@ -2272,6 +1377,11 @@ public:
pos += config.comment_open.size();
return make_token(Token::Kind::CommentOpen);
}
+ case State::CommentStartForceLstrip: {
+ state = State::CommentBody;
+ pos += config.comment_open_force_lstrip.size();
+ return make_token(Token::Kind::CommentOpen);
+ }
case State::ExpressionBody:
return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip);
case State::LineBody:
@@ -2280,16 +1390,21 @@ public:
return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks);
case State::CommentBody: {
// fast-scan to comment close
- size_t end = m_in.substr(pos).find(config.comment_close);
- if (end == nonstd::string_view::npos) {
+ const size_t end = m_in.substr(pos).find(config.comment_close);
+ if (end == std::string_view::npos) {
pos = m_in.size();
return make_token(Token::Kind::Eof);
}
+
+ // Check for trim pattern
+ const bool must_rstrip = inja::string_view::starts_with(m_in.substr(pos + end - 1), config.comment_close_force_rstrip);
+
// return the entire comment in the close token
state = State::Text;
pos += end + config.comment_close.size();
Token tok = make_token(Token::Kind::CommentClose);
- if (config.trim_blocks) {
+
+ if (must_rstrip || config.trim_blocks) {
skip_whitespaces_and_first_newline();
}
return tok;
@@ -2297,7 +1412,7 @@ public:
}
}
- const LexerConfig &get_config() const {
+ const LexerConfig& get_config() const {
return config;
}
};
@@ -2307,467 +1422,25 @@ public:
#endif // INCLUDE_INJA_LEXER_HPP_
// #include "node.hpp"
-// Copyright (c) 2020 Pantor. All rights reserved.
-
-#ifndef INCLUDE_INJA_NODE_HPP_
-#define INCLUDE_INJA_NODE_HPP_
-
-#include <string>
-#include <utility>
-
-#include <nlohmann/json.hpp>
-
-// #include "function_storage.hpp"
-
-// #include "string_view.hpp"
-
-
-
-namespace inja {
-
-class NodeVisitor;
-class BlockNode;
-class TextNode;
-class ExpressionNode;
-class LiteralNode;
-class JsonNode;
-class FunctionNode;
-class ExpressionListNode;
-class StatementNode;
-class ForStatementNode;
-class ForArrayStatementNode;
-class ForObjectStatementNode;
-class IfStatementNode;
-class IncludeStatementNode;
-class SetStatementNode;
-
-
-class NodeVisitor {
-public:
- virtual void visit(const BlockNode& node) = 0;
- virtual void visit(const TextNode& node) = 0;
- virtual void visit(const ExpressionNode& node) = 0;
- virtual void visit(const LiteralNode& node) = 0;
- virtual void visit(const JsonNode& node) = 0;
- virtual void visit(const FunctionNode& node) = 0;
- virtual void visit(const ExpressionListNode& node) = 0;
- virtual void visit(const StatementNode& node) = 0;
- virtual void visit(const ForStatementNode& node) = 0;
- virtual void visit(const ForArrayStatementNode& node) = 0;
- virtual void visit(const ForObjectStatementNode& node) = 0;
- virtual void visit(const IfStatementNode& node) = 0;
- virtual void visit(const IncludeStatementNode& node) = 0;
- virtual void visit(const SetStatementNode& node) = 0;
-};
-
-/*!
- * \brief Base node class for the abstract syntax tree (AST).
- */
-class AstNode {
-public:
- virtual void accept(NodeVisitor& v) const = 0;
-
- size_t pos;
-
- AstNode(size_t pos) : pos(pos) { }
- virtual ~AstNode() { };
-};
-
-
-class BlockNode : public AstNode {
-public:
- std::vector<std::shared_ptr<AstNode>> nodes;
-
- explicit BlockNode() : AstNode(0) {}
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class TextNode : public AstNode {
-public:
- const size_t length;
-
- explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) { }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class ExpressionNode : public AstNode {
-public:
- explicit ExpressionNode(size_t pos) : AstNode(pos) {}
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class LiteralNode : public ExpressionNode {
-public:
- const nlohmann::json value;
-
- explicit LiteralNode(const nlohmann::json& value, size_t pos) : ExpressionNode(pos), value(value) { }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class JsonNode : public ExpressionNode {
-public:
- const std::string name;
- const json::json_pointer ptr;
-
- static std::string convert_dot_to_json_ptr(nonstd::string_view ptr_name) {
- std::string result;
- do {
- nonstd::string_view part;
- std::tie(part, ptr_name) = string_view::split(ptr_name, '.');
- result.push_back('/');
- result.append(part.begin(), part.end());
- } while (!ptr_name.empty());
- return result;
- }
-
- explicit JsonNode(nonstd::string_view ptr_name, size_t pos) : ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_json_ptr(ptr_name))) { }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class FunctionNode : public ExpressionNode {
- using Op = FunctionStorage::Operation;
-
-public:
- enum class Associativity {
- Left,
- Right,
- };
-
- unsigned int precedence;
- Associativity associativity;
-
- Op operation;
-
- std::string name;
- int number_args; // Should also be negative -> -1 for unknown number
- CallbackFunction callback;
-
- explicit FunctionNode(nonstd::string_view name, size_t pos) : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) { }
- explicit FunctionNode(Op operation, size_t pos) : ExpressionNode(pos), operation(operation), number_args(1) {
- switch (operation) {
- case Op::Not: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::And: {
- precedence = 1;
- associativity = Associativity::Left;
- } break;
- case Op::Or: {
- precedence = 1;
- associativity = Associativity::Left;
- } break;
- case Op::In: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Equal: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::NotEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Greater: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::GreaterEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Less: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::LessEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Add: {
- precedence = 3;
- associativity = Associativity::Left;
- } break;
- case Op::Subtract: {
- precedence = 3;
- associativity = Associativity::Left;
- } break;
- case Op::Multiplication: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::Division: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::Power: {
- precedence = 5;
- associativity = Associativity::Right;
- } break;
- case Op::Modulo: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::AtId: {
- precedence = 8;
- associativity = Associativity::Left;
- } break;
- default: {
- precedence = 1;
- associativity = Associativity::Left;
- }
- }
- }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class ExpressionListNode : public AstNode {
-public:
- std::vector<std::shared_ptr<ExpressionNode>> rpn_output;
-
- explicit ExpressionListNode() : AstNode(0) { }
- explicit ExpressionListNode(size_t pos) : AstNode(pos) { }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class StatementNode : public AstNode {
-public:
- StatementNode(size_t pos) : AstNode(pos) { }
-
- virtual void accept(NodeVisitor& v) const = 0;
-};
-
-class ForStatementNode : public StatementNode {
-public:
- ExpressionListNode condition;
- BlockNode body;
- BlockNode *const parent;
-
- ForStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent) { }
-
- virtual void accept(NodeVisitor& v) const = 0;
-};
-
-class ForArrayStatementNode : public ForStatementNode {
-public:
- const std::string value;
-
- explicit ForArrayStatementNode(const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), value(value) { }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class ForObjectStatementNode : public ForStatementNode {
-public:
- const std::string key;
- const std::string value;
-
- explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), key(key), value(value) { }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class IfStatementNode : public StatementNode {
-public:
- ExpressionListNode condition;
- BlockNode true_statement;
- BlockNode false_statement;
- BlockNode *const parent;
-
- const bool is_nested;
- bool has_false_statement {false};
-
- explicit IfStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(false) { }
- explicit IfStatementNode(bool is_nested, BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(is_nested) { }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
-};
-
-class IncludeStatementNode : public StatementNode {
-public:
- const std::string file;
-
- explicit IncludeStatementNode(const std::string& file, size_t pos) : StatementNode(pos), file(file) { }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- };
-};
-
-class SetStatementNode : public StatementNode {
-public:
- const std::string key;
- ExpressionListNode expression;
-
- explicit SetStatementNode(const std::string& key, size_t pos) : StatementNode(pos), key(key) { }
-
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- };
-};
-
-} // namespace inja
-
-#endif // INCLUDE_INJA_NODE_HPP_
// #include "template.hpp"
-// Copyright (c) 2019 Pantor. All rights reserved.
-
-#ifndef INCLUDE_INJA_TEMPLATE_HPP_
-#define INCLUDE_INJA_TEMPLATE_HPP_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-// #include "node.hpp"
-
-// #include "statistics.hpp"
-// Copyright (c) 2019 Pantor. All rights reserved.
-
-#ifndef INCLUDE_INJA_STATISTICS_HPP_
-#define INCLUDE_INJA_STATISTICS_HPP_
-
-// #include "node.hpp"
-
-
-
-namespace inja {
-
-/*!
- * \brief A class for counting statistics on a Template.
- */
-class StatisticsVisitor : public NodeVisitor {
- void visit(const BlockNode& node) {
- for (auto& n : node.nodes) {
- n->accept(*this);
- }
- }
-
- void visit(const TextNode&) { }
- void visit(const ExpressionNode&) { }
- void visit(const LiteralNode&) { }
-
- void visit(const JsonNode&) {
- variable_counter += 1;
- }
-
- void visit(const FunctionNode&) { }
-
- void visit(const ExpressionListNode& node) {
- for (auto& n : node.rpn_output) {
- n->accept(*this);
- }
- }
-
- void visit(const StatementNode&) { }
- void visit(const ForStatementNode&) { }
-
- void visit(const ForArrayStatementNode& node) {
- node.condition.accept(*this);
- node.body.accept(*this);
- }
-
- void visit(const ForObjectStatementNode& node) {
- node.condition.accept(*this);
- node.body.accept(*this);
- }
-
- void visit(const IfStatementNode& node) {
- node.condition.accept(*this);
- node.true_statement.accept(*this);
- node.false_statement.accept(*this);
- }
-
- void visit(const IncludeStatementNode&) { }
-
- void visit(const SetStatementNode&) { }
-
-public:
- unsigned int variable_counter;
-
- explicit StatisticsVisitor() : variable_counter(0) { }
-};
-
-} // namespace inja
-
-#endif // INCLUDE_INJA_STATISTICS_HPP_
-
-
-
-namespace inja {
-
-/*!
- * \brief The main inja Template.
- */
-struct Template {
- BlockNode root;
- std::string content;
-
- explicit Template() { }
- explicit Template(const std::string& content): content(content) { }
-
- /// Return number of variables (total number, not distinct ones) in the template
- int count_variables() {
- auto statistic_visitor = StatisticsVisitor();
- root.accept(statistic_visitor);
- return statistic_visitor.variable_counter;
- }
-};
-
-using TemplateStorage = std::map<std::string, Template>;
-
-} // namespace inja
-
-#endif // INCLUDE_INJA_TEMPLATE_HPP_
// #include "token.hpp"
// #include "utils.hpp"
-#include <nlohmann/json.hpp>
-
namespace inja {
/*!
* \brief Class for parsing an inja Template.
*/
class Parser {
- const ParserConfig &config;
+ const ParserConfig& config;
Lexer lexer;
- TemplateStorage &template_storage;
- const FunctionStorage &function_storage;
+ TemplateStorage& template_storage;
+ const FunctionStorage& function_storage;
Token tok, peek_tok;
bool have_peek_tok {false};
@@ -2776,21 +1449,23 @@ class Parser {
size_t current_bracket_level {0};
size_t current_brace_level {0};
- nonstd::string_view json_literal_start;
+ std::string_view literal_start;
- BlockNode *current_block {nullptr};
- ExpressionListNode *current_expression_list {nullptr};
+ BlockNode* current_block {nullptr};
+ ExpressionListNode* current_expression_list {nullptr};
std::stack<std::pair<FunctionNode*, size_t>> function_stack;
+ std::vector<std::shared_ptr<ExpressionNode>> arguments;
std::stack<std::shared_ptr<FunctionNode>> operator_stack;
std::stack<IfStatementNode*> if_statement_stack;
std::stack<ForStatementNode*> for_statement_stack;
+ std::stack<BlockStatementNode*> block_statement_stack;
- void throw_parser_error(const std::string &message) {
+ inline void throw_parser_error(const std::string& message) const {
INJA_THROW(ParserError(message, lexer.current_position()));
}
- void get_next_token() {
+ inline void get_next_token() {
if (have_peek_tok) {
tok = peek_tok;
have_peek_tok = false;
@@ -2799,49 +1474,108 @@ class Parser {
}
}
- void get_peek_token() {
+ inline void get_peek_token() {
if (!have_peek_tok) {
peek_tok = lexer.scan();
have_peek_tok = true;
}
}
- void add_json_literal(const char* content_ptr) {
- nonstd::string_view json_text(json_literal_start.data(), tok.text.data() - json_literal_start.data() + tok.text.size());
- current_expression_list->rpn_output.emplace_back(std::make_shared<LiteralNode>(json::parse(json_text), json_text.data() - content_ptr));
+ inline void add_literal(const char* content_ptr) {
+ std::string_view data_text(literal_start.data(), tok.text.data() - literal_start.data() + tok.text.size());
+ arguments.emplace_back(std::make_shared<LiteralNode>(data_text, data_text.data() - content_ptr));
+ }
+
+ inline void add_operator() {
+ auto function = operator_stack.top();
+ operator_stack.pop();
+
+ for (int i = 0; i < function->number_args; ++i) {
+ function->arguments.insert(function->arguments.begin(), arguments.back());
+ arguments.pop_back();
+ }
+ arguments.emplace_back(function);
}
- bool parse_expression(Template &tmpl, Token::Kind closing) {
+ void add_to_template_storage(std::string_view path, std::string& template_name) {
+ if (template_storage.find(template_name) != template_storage.end()) {
+ return;
+ }
+
+ std::string original_path = static_cast<std::string>(path);
+ std::string original_name = template_name;
+
+ if (config.search_included_templates_in_files) {
+ // Build the relative path
+ template_name = original_path + original_name;
+ if (template_name.compare(0, 2, "./") == 0) {
+ template_name.erase(0, 2);
+ }
+
+ if (template_storage.find(template_name) == template_storage.end()) {
+ // Load file
+ std::ifstream file;
+ file.open(template_name);
+ if (!file.fail()) {
+ std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+
+ auto include_template = Template(text);
+ template_storage.emplace(template_name, include_template);
+ parse_into_template(template_storage[template_name], template_name);
+ return;
+ } else if (!config.include_callback) {
+ INJA_THROW(FileError("failed accessing file at '" + template_name + "'"));
+ }
+ }
+ }
+
+ // Try include callback
+ if (config.include_callback) {
+ auto include_template = config.include_callback(original_path, original_name);
+ template_storage.emplace(template_name, include_template);
+ }
+ }
+
+ std::string parse_filename(const Token& tok) const {
+ if (tok.kind != Token::Kind::String) {
+ throw_parser_error("expected string, got '" + tok.describe() + "'");
+ }
+
+ if (tok.text.length() < 2) {
+ throw_parser_error("expected filename, got '" + static_cast<std::string>(tok.text) + "'");
+ }
+
+ // Remove first and last character ""
+ return std::string {tok.text.substr(1, tok.text.length() - 2)};
+ }
+
+ bool parse_expression(Template& tmpl, Token::Kind closing) {
while (tok.kind != closing && tok.kind != Token::Kind::Eof) {
// Literals
switch (tok.kind) {
case Token::Kind::String: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
+ literal_start = tok.text;
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::Number: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
+ literal_start = tok.text;
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::LeftBracket: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
+ literal_start = tok.text;
}
current_bracket_level += 1;
-
} break;
case Token::Kind::LeftBrace: {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
+ literal_start = tok.text;
}
current_brace_level += 1;
-
} break;
case Token::Kind::RightBracket: {
if (current_bracket_level == 0) {
@@ -2850,9 +1584,8 @@ class Parser {
current_bracket_level -= 1;
if (current_brace_level == 0 && current_bracket_level == 0) {
- add_json_literal(tmpl.content.c_str());
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::RightBrace: {
if (current_brace_level == 0) {
@@ -2861,35 +1594,35 @@ class Parser {
current_brace_level -= 1;
if (current_brace_level == 0 && current_bracket_level == 0) {
- add_json_literal(tmpl.content.c_str());
+ add_literal(tmpl.content.c_str());
}
-
} break;
case Token::Kind::Id: {
get_peek_token();
- // Json Literal
- if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") || tok.text == static_cast<decltype(tok.text)>("null")) {
+ // Data Literal
+ if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") ||
+ tok.text == static_cast<decltype(tok.text)>("null")) {
if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
+ literal_start = tok.text;
+ add_literal(tmpl.content.c_str());
}
- // Operator
+ // Operator
} else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") {
goto parse_operator;
- // Functions
+ // Functions
} else if (peek_tok.kind == Token::Kind::LeftParen) {
operator_stack.emplace(std::make_shared<FunctionNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
- function_stack.emplace(operator_stack.top().get(), current_paren_level);
+ function_stack.emplace(operator_stack.top().get(), current_paren_level);
- // Variables
+ // Variables
} else {
- current_expression_list->rpn_output.emplace_back(std::make_shared<JsonNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
+ arguments.emplace_back(std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
}
- // Operators
+ // Operators
} break;
case Token::Kind::Equal:
case Token::Kind::NotEqual:
@@ -2905,7 +1638,7 @@ class Parser {
case Token::Kind::Percent:
case Token::Kind::Dot: {
- parse_operator:
+ parse_operator:
FunctionStorage::Operation operation;
switch (tok.kind) {
case Token::Kind::Id: {
@@ -2966,13 +1699,14 @@ class Parser {
}
auto function_node = std::make_shared<FunctionNode>(operation, tok.text.data() - tmpl.content.c_str());
- while (!operator_stack.empty() && ((operator_stack.top()->precedence > function_node->precedence) || (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) && (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ while (!operator_stack.empty() &&
+ ((operator_stack.top()->precedence > function_node->precedence) ||
+ (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) &&
+ (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
+ add_operator();
}
operator_stack.emplace(function_node);
-
} break;
case Token::Kind::Comma: {
if (current_brace_level == 0 && current_bracket_level == 0) {
@@ -2982,13 +1716,11 @@ class Parser {
function_stack.top().first->number_args += 1;
}
-
} break;
case Token::Kind::Colon: {
if (current_brace_level == 0 && current_bracket_level == 0) {
throw_parser_error("unexpected ':'");
}
-
} break;
case Token::Kind::LeftParen: {
current_paren_level += 1;
@@ -3000,13 +1732,11 @@ class Parser {
function_stack.top().first->number_args = 0;
}
}
-
} break;
case Token::Kind::RightParen: {
current_paren_level -= 1;
while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ add_operator();
}
if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) {
@@ -3028,8 +1758,7 @@ class Parser {
throw_parser_error("internal error at function " + func->name);
}
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ add_operator();
function_stack.pop();
}
}
@@ -3041,14 +1770,20 @@ class Parser {
}
while (!operator_stack.empty()) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
+ add_operator();
+ }
+
+ if (arguments.size() == 1) {
+ current_expression_list->root = arguments[0];
+ arguments = {};
+ } else if (arguments.size() > 1) {
+ throw_parser_error("malformed expression");
}
return true;
}
- bool parse_statement(Template &tmpl, Token::Kind closing, nonstd::string_view path) {
+ bool parse_statement(Template& tmpl, Token::Kind closing, std::string_view path) {
if (tok.kind != Token::Kind::Id) {
return false;
}
@@ -3065,12 +1800,11 @@ class Parser {
if (!parse_expression(tmpl, closing)) {
return false;
}
-
} else if (tok.text == static_cast<decltype(tok.text)>("else")) {
if (if_statement_stack.empty()) {
throw_parser_error("else without matching if");
}
- auto &if_statement_data = if_statement_stack.top();
+ auto& if_statement_data = if_statement_stack.top();
get_next_token();
if_statement_data->has_false_statement = true;
@@ -3090,7 +1824,6 @@ class Parser {
return false;
}
}
-
} else if (tok.text == static_cast<decltype(tok.text)>("endif")) {
if (if_statement_stack.empty()) {
throw_parser_error("endif without matching if");
@@ -3101,12 +1834,40 @@ class Parser {
if_statement_stack.pop();
}
- auto &if_statement_data = if_statement_stack.top();
+ auto& if_statement_data = if_statement_stack.top();
get_next_token();
current_block = if_statement_data->parent;
if_statement_stack.pop();
+ } else if (tok.text == static_cast<decltype(tok.text)>("block")) {
+ get_next_token();
+
+ if (tok.kind != Token::Kind::Id) {
+ throw_parser_error("expected block name, got '" + tok.describe() + "'");
+ }
+
+ const std::string block_name = static_cast<std::string>(tok.text);
+
+ auto block_statement_node = std::make_shared<BlockStatementNode>(current_block, block_name, tok.text.data() - tmpl.content.c_str());
+ current_block->nodes.emplace_back(block_statement_node);
+ block_statement_stack.emplace(block_statement_node.get());
+ current_block = &block_statement_node->block;
+ auto success = tmpl.block_storage.emplace(block_name, block_statement_node);
+ if (!success.second) {
+ throw_parser_error("block with the name '" + block_name + "' does already exist");
+ }
+ get_next_token();
+ } else if (tok.text == static_cast<decltype(tok.text)>("endblock")) {
+ if (block_statement_stack.empty()) {
+ throw_parser_error("endblock without matching block");
+ }
+
+ auto& block_statement_data = block_statement_stack.top();
+ get_next_token();
+
+ current_block = block_statement_data->parent;
+ block_statement_stack.pop();
} else if (tok.text == static_cast<decltype(tok.text)>("for")) {
get_next_token();
@@ -3130,11 +1891,13 @@ class Parser {
value_token = tok;
get_next_token();
- for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text), static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
+ for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text), static_cast<std::string>(value_token.text),
+ current_block, tok.text.data() - tmpl.content.c_str());
- // Array type
+ // Array type
} else {
- for_statement_node = std::make_shared<ForArrayStatementNode>(static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
+ for_statement_node =
+ std::make_shared<ForArrayStatementNode>(static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
}
current_block->nodes.emplace_back(for_statement_node);
@@ -3150,44 +1913,34 @@ class Parser {
if (!parse_expression(tmpl, closing)) {
return false;
}
-
} else if (tok.text == static_cast<decltype(tok.text)>("endfor")) {
if (for_statement_stack.empty()) {
throw_parser_error("endfor without matching for");
}
- auto &for_statement_data = for_statement_stack.top();
+ auto& for_statement_data = for_statement_stack.top();
get_next_token();
current_block = for_statement_data->parent;
for_statement_stack.pop();
-
} else if (tok.text == static_cast<decltype(tok.text)>("include")) {
get_next_token();
- if (tok.kind != Token::Kind::String) {
- throw_parser_error("expected string, got '" + tok.describe() + "'");
- }
+ std::string template_name = parse_filename(tok);
+ add_to_template_storage(path, template_name);
- // Build the relative path
- json json_name = json::parse(tok.text);
- std::string pathname = static_cast<std::string>(path);
- pathname += json_name.get_ref<const std::string &>();
- if (pathname.compare(0, 2, "./") == 0) {
- pathname.erase(0, 2);
- }
- // sys::path::remove_dots(pathname, true, sys::path::Style::posix);
+ current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
- if (config.search_included_templates_in_files && template_storage.find(pathname) == template_storage.end()) {
- auto include_template = Template(load_file(pathname));
- template_storage.emplace(pathname, include_template);
- parse_into_template(template_storage[pathname], pathname);
- }
+ get_next_token();
+ } else if (tok.text == static_cast<decltype(tok.text)>("extends")) {
+ get_next_token();
- current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(pathname, tok.text.data() - tmpl.content.c_str()));
+ std::string template_name = parse_filename(tok);
+ add_to_template_storage(path, template_name);
- get_next_token();
+ current_block->nodes.emplace_back(std::make_shared<ExtendsStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
+ get_next_token();
} else if (tok.text == static_cast<decltype(tok.text)>("set")) {
get_next_token();
@@ -3210,14 +1963,13 @@ class Parser {
if (!parse_expression(tmpl, closing)) {
return false;
}
-
} else {
return false;
}
return true;
}
- void parse_into(Template &tmpl, nonstd::string_view path) {
+ void parse_into(Template& tmpl, std::string_view path) {
lexer.start(tmpl.content);
current_block = &tmpl.root;
@@ -3231,7 +1983,8 @@ class Parser {
if (!for_statement_stack.empty()) {
throw_parser_error("unmatched for");
}
- } return;
+ }
+ return;
case Token::Kind::Text: {
current_block->nodes.emplace_back(std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
} break;
@@ -3281,33 +2034,35 @@ class Parser {
}
}
-
public:
- explicit Parser(const ParserConfig &parser_config, const LexerConfig &lexer_config,
- TemplateStorage &template_storage, const FunctionStorage &function_storage)
- : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) { }
+ explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& template_storage,
+ const FunctionStorage& function_storage)
+ : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) {}
- Template parse(nonstd::string_view input, nonstd::string_view path) {
+ Template parse(std::string_view input, std::string_view path) {
auto result = Template(static_cast<std::string>(input));
parse_into(result, path);
return result;
}
- Template parse(nonstd::string_view input) {
+ Template parse(std::string_view input) {
return parse(input, "./");
}
- void parse_into_template(Template& tmpl, nonstd::string_view filename) {
- nonstd::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
+ void parse_into_template(Template& tmpl, std::string_view filename) {
+ std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
// StringRef path = sys::path::parent_path(filename);
auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage);
sub_parser.parse_into(tmpl, path);
}
- std::string load_file(nonstd::string_view filename) {
+ std::string load_file(const std::string& filename) {
std::ifstream file;
- open_file_or_throw(static_cast<std::string>(filename), file);
+ file.open(filename);
+ if (file.fail()) {
+ INJA_THROW(FileError("failed accessing file at '" + filename + "'"));
+ }
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return text;
}
@@ -3318,8 +2073,6 @@ public:
#endif // INCLUDE_INJA_PARSER_HPP_
// #include "renderer.hpp"
-// Copyright (c) 2020 Pantor. All rights reserved.
-
#ifndef INCLUDE_INJA_RENDERER_HPP_
#define INCLUDE_INJA_RENDERER_HPP_
@@ -3329,8 +2082,6 @@ public:
#include <utility>
#include <vector>
-#include <nlohmann/json.hpp>
-
// #include "config.hpp"
// #include "exceptions.hpp"
@@ -3347,25 +2098,31 @@ namespace inja {
/*!
* \brief Class for rendering a Template with data.
*/
-class Renderer : public NodeVisitor {
+class Renderer : public NodeVisitor {
using Op = FunctionStorage::Operation;
const RenderConfig config;
- const Template *current_template;
- const TemplateStorage &template_storage;
- const FunctionStorage &function_storage;
+ const TemplateStorage& template_storage;
+ const FunctionStorage& function_storage;
+
+ const Template* current_template;
+ size_t current_level {0};
+ std::vector<const Template*> template_stack;
+ std::vector<const BlockStatementNode*> block_statement_stack;
- const json *json_input;
- std::ostream *output_stream;
+ const json* data_input;
+ std::ostream* output_stream;
- json json_additional_data;
- json* current_loop_data = &json_additional_data["loop"];
+ json additional_data;
+ json* current_loop_data = &additional_data["loop"];
- std::vector<std::shared_ptr<json>> json_tmp_stack;
- std::stack<const json*> json_eval_stack;
- std::stack<const JsonNode*> not_found_stack;
+ std::vector<std::shared_ptr<json>> data_tmp_stack;
+ std::stack<const json*> data_eval_stack;
+ std::stack<const DataNode*> not_found_stack;
- bool truthy(const json* data) const {
+ bool break_rendering {false};
+
+ static bool truthy(const json* data) {
if (data->is_boolean()) {
return data->get<bool>();
} else if (data->is_number()) {
@@ -3376,7 +2133,7 @@ class Renderer : public NodeVisitor {
return !data->empty();
}
- void print_json(const std::shared_ptr<json> value) {
+ void print_data(const std::shared_ptr<json> value) {
if (value->is_string()) {
*output_stream << value->get_ref<const json::string_t&>();
} else if (value->is_number_integer()) {
@@ -3388,18 +2145,20 @@ class Renderer : public NodeVisitor {
}
const std::shared_ptr<json> eval_expression_list(const ExpressionListNode& expression_list) {
- for (auto& expression : expression_list.rpn_output) {
- expression->accept(*this);
+ if (!expression_list.root) {
+ throw_renderer_error("empty expression", expression_list);
}
- if (json_eval_stack.empty()) {
+ expression_list.root->accept(*this);
+
+ if (data_eval_stack.empty()) {
throw_renderer_error("empty expression", expression_list);
- } else if (json_eval_stack.size() != 1) {
+ } else if (data_eval_stack.size() != 1) {
throw_renderer_error("malformed expression", expression_list);
}
- auto result = json_eval_stack.top();
- json_eval_stack.pop();
+ const auto result = data_eval_stack.top();
+ data_eval_stack.pop();
if (!result) {
if (not_found_stack.empty()) {
@@ -3414,51 +2173,68 @@ class Renderer : public NodeVisitor {
return std::make_shared<json>(*result);
}
- void throw_renderer_error(const std::string &message, const AstNode& node) {
+ void throw_renderer_error(const std::string& message, const AstNode& node) {
SourceLocation loc = get_source_location(current_template->content, node.pos);
INJA_THROW(RenderError(message, loc));
}
- template<size_t N, bool throw_not_found=true>
- std::array<const json*, N> get_arguments(const AstNode& node) {
- if (json_eval_stack.size() < N) {
- throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node);
+ void make_result(const json&& result) {
+ auto result_ptr = std::make_shared<json>(result);
+ data_tmp_stack.push_back(result_ptr);
+ data_eval_stack.push(result_ptr.get());
+ }
+
+ template <size_t N, size_t N_start = 0, bool throw_not_found = true> std::array<const json*, N> get_arguments(const FunctionNode& node) {
+ if (node.arguments.size() < N_start + N) {
+ throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " + std::to_string(node.arguments.size()), node);
+ }
+
+ for (size_t i = N_start; i < N_start + N; i += 1) {
+ node.arguments[i]->accept(*this);
+ }
+
+ if (data_eval_stack.size() < N) {
+ throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
}
std::array<const json*, N> result;
for (size_t i = 0; i < N; i += 1) {
- result[N - i - 1] = json_eval_stack.top();
- json_eval_stack.pop();
+ result[N - i - 1] = data_eval_stack.top();
+ data_eval_stack.pop();
if (!result[N - i - 1]) {
- auto json_node = not_found_stack.top();
+ const auto data_node = not_found_stack.top();
not_found_stack.pop();
if (throw_not_found) {
- throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
+ throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
}
}
}
return result;
}
- template<bool throw_not_found=true>
- Arguments get_argument_vector(size_t N, const AstNode& node) {
- if (json_eval_stack.size() < N) {
- throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node);
+ template <bool throw_not_found = true> Arguments get_argument_vector(const FunctionNode& node) {
+ const size_t N = node.arguments.size();
+ for (auto a : node.arguments) {
+ a->accept(*this);
+ }
+
+ if (data_eval_stack.size() < N) {
+ throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
}
Arguments result {N};
for (size_t i = 0; i < N; i += 1) {
- result[N - i - 1] = json_eval_stack.top();
- json_eval_stack.pop();
+ result[N - i - 1] = data_eval_stack.top();
+ data_eval_stack.pop();
if (!result[N - i - 1]) {
- auto json_node = not_found_stack.top();
+ const auto data_node = not_found_stack.top();
not_found_stack.pop();
if (throw_not_found) {
- throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
+ throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
}
}
}
@@ -3468,6 +2244,10 @@ class Renderer : public NodeVisitor {
void visit(const BlockNode& node) {
for (auto& n : node.nodes) {
n->accept(*this);
+
+ if (break_rendering) {
+ break;
+ }
}
}
@@ -3475,325 +2255,294 @@ class Renderer : public NodeVisitor {
output_stream->write(current_template->content.c_str() + node.pos, node.length);
}
- void visit(const ExpressionNode&) { }
+ void visit(const ExpressionNode&) {}
void visit(const LiteralNode& node) {
- json_eval_stack.push(&node.value);
+ data_eval_stack.push(&node.value);
}
- void visit(const JsonNode& node) {
- if (json_additional_data.contains(node.ptr)) {
- json_eval_stack.push(&(json_additional_data[node.ptr]));
-
- } else if (json_input->contains(node.ptr)) {
- json_eval_stack.push(&(*json_input)[node.ptr]);
-
+ void visit(const DataNode& node) {
+ if (additional_data.contains(node.ptr)) {
+ data_eval_stack.push(&(additional_data[node.ptr]));
+ } else if (data_input->contains(node.ptr)) {
+ data_eval_stack.push(&(*data_input)[node.ptr]);
} else {
// Try to evaluate as a no-argument callback
- auto function_data = function_storage.find_function(node.name, 0);
+ const auto function_data = function_storage.find_function(node.name, 0);
if (function_data.operation == FunctionStorage::Operation::Callback) {
Arguments empty_args {};
- auto value = std::make_shared<json>(function_data.callback(empty_args));
- json_tmp_stack.push_back(value);
- json_eval_stack.push(value.get());
-
+ const auto value = std::make_shared<json>(function_data.callback(empty_args));
+ data_tmp_stack.push_back(value);
+ data_eval_stack.push(value.get());
} else {
- json_eval_stack.push(nullptr);
+ data_eval_stack.push(nullptr);
not_found_stack.emplace(&node);
}
}
}
void visit(const FunctionNode& node) {
- std::shared_ptr<json> result_ptr;
-
switch (node.operation) {
case Op::Not: {
- auto args = get_arguments<1>(node);
- result_ptr = std::make_shared<json>(!truthy(args[0]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<1>(node);
+ make_result(!truthy(args[0]));
} break;
case Op::And: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(truthy(args[0]) && truthy(args[1]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0]));
} break;
case Op::Or: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(truthy(args[0]) || truthy(args[1]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0]));
} break;
case Op::In: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
} break;
case Op::Equal: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] == *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] == *args[1]);
} break;
case Op::NotEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] != *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] != *args[1]);
} break;
case Op::Greater: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] > *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] > *args[1]);
} break;
case Op::GreaterEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] >= *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] >= *args[1]);
} break;
case Op::Less: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] < *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] < *args[1]);
} break;
case Op::LessEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] <= *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(*args[0] <= *args[1]);
} break;
case Op::Add: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_string() && args[1]->is_string()) {
- result_ptr = std::make_shared<json>(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
} else if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() + args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<int>() + args[1]->get<int>());
} else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() + args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<double>() + args[1]->get<double>());
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Subtract: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() - args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<int>() - args[1]->get<int>());
} else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() - args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<double>() - args[1]->get<double>());
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Multiplication: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() * args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<int>() * args[1]->get<int>());
} else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() * args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
+ make_result(args[0]->get<double>() * args[1]->get<double>());
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Division: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[1]->get<double>() == 0) {
throw_renderer_error("division by zero", node);
}
- result_ptr = std::make_shared<json>(args[0]->get<double>() / args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(args[0]->get<double>() / args[1]->get<double>());
} break;
case Op::Power: {
- auto args = get_arguments<2>(node);
+ const auto args = get_arguments<2>(node);
if (args[0]->is_number_integer() && args[1]->get<int>() >= 0) {
- int result = std::pow(args[0]->get<int>(), args[1]->get<int>());
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
+ int result = static_cast<int>(std::pow(args[0]->get<int>(), args[1]->get<int>()));
+ make_result(result);
} else {
double result = std::pow(args[0]->get<double>(), args[1]->get<int>());
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
+ make_result(result);
}
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Modulo: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(args[0]->get<int>() % args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ make_result(args[0]->get<int>() % args[1]->get<int>());
} break;
case Op::AtId: {
- json_eval_stack.pop(); // Pop id nullptr
- auto container = get_arguments<1, false>(node)[0];
+ const auto container = get_arguments<1, 0, false>(node)[0];
+ node.arguments[1]->accept(*this);
if (not_found_stack.empty()) {
throw_renderer_error("could not find element with given name", node);
}
- auto id_node = not_found_stack.top();
+ const auto id_node = not_found_stack.top();
not_found_stack.pop();
- json_eval_stack.push(&container->at(id_node->name));
+ data_eval_stack.pop();
+ data_eval_stack.push(&container->at(id_node->name));
} break;
case Op::At: {
- auto args = get_arguments<2>(node);
- json_eval_stack.push(&args[0]->at(args[1]->get<int>()));
+ const auto args = get_arguments<2>(node);
+ if (args[0]->is_object()) {
+ data_eval_stack.push(&args[0]->at(args[1]->get<std::string>()));
+ } else {
+ data_eval_stack.push(&args[0]->at(args[1]->get<int>()));
+ }
} break;
case Op::Default: {
- auto default_arg = get_arguments<1>(node)[0];
- auto test_arg = get_arguments<1, false>(node)[0];
- json_eval_stack.push(test_arg ? test_arg : default_arg);
+ const auto test_arg = get_arguments<1, 0, false>(node)[0];
+ data_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]);
} break;
case Op::DivisibleBy: {
- auto args = get_arguments<2>(node);
- int divisor = args[1]->get<int>();
- result_ptr = std::make_shared<json>((divisor != 0) && (args[0]->get<int>() % divisor == 0));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ const int divisor = args[1]->get<int>();
+ make_result((divisor != 0) && (args[0]->get<int>() % divisor == 0));
} break;
case Op::Even: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
} break;
case Op::Exists: {
- auto &&name = get_arguments<1>(node)[0]->get_ref<const std::string &>();
- result_ptr = std::make_shared<json>(json_input->contains(json::json_pointer(JsonNode::convert_dot_to_json_ptr(name))));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ auto&& name = get_arguments<1>(node)[0]->get_ref<const std::string&>();
+ make_result(data_input->contains(json::json_pointer(DataNode::convert_dot_to_ptr(name))));
} break;
case Op::ExistsInObject: {
- auto args = get_arguments<2>(node);
- auto &&name = args[1]->get_ref<const std::string &>();
- result_ptr = std::make_shared<json>(args[0]->find(name) != args[0]->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ auto&& name = args[1]->get_ref<const std::string&>();
+ make_result(args[0]->find(name) != args[0]->end());
} break;
case Op::First: {
- auto result = &get_arguments<1>(node)[0]->front();
- json_eval_stack.push(result);
+ const auto result = &get_arguments<1>(node)[0]->front();
+ data_eval_stack.push(result);
} break;
case Op::Float: {
- result_ptr = std::make_shared<json>(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
} break;
case Op::Int: {
- result_ptr = std::make_shared<json>(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
} break;
case Op::Last: {
- auto result = &get_arguments<1>(node)[0]->back();
- json_eval_stack.push(result);
+ const auto result = &get_arguments<1>(node)[0]->back();
+ data_eval_stack.push(result);
} break;
case Op::Length: {
- auto val = get_arguments<1>(node)[0];
+ const auto val = get_arguments<1>(node)[0];
if (val->is_string()) {
- result_ptr = std::make_shared<json>(val->get_ref<const std::string &>().length());
+ make_result(val->get_ref<const std::string&>().length());
} else {
- result_ptr = std::make_shared<json>(val->size());
+ make_result(val->size());
}
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
} break;
case Op::Lower: {
std::string result = get_arguments<1>(node)[0]->get<std::string>();
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::move(result));
} break;
case Op::Max: {
- auto args = get_arguments<1>(node);
- auto result = std::max_element(args[0]->begin(), args[0]->end());
- json_eval_stack.push(&(*result));
+ const auto args = get_arguments<1>(node);
+ const auto result = std::max_element(args[0]->begin(), args[0]->end());
+ data_eval_stack.push(&(*result));
} break;
case Op::Min: {
- auto args = get_arguments<1>(node);
- auto result = std::min_element(args[0]->begin(), args[0]->end());
- json_eval_stack.push(&(*result));
+ const auto args = get_arguments<1>(node);
+ const auto result = std::min_element(args[0]->begin(), args[0]->end());
+ data_eval_stack.push(&(*result));
} break;
case Op::Odd: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
} break;
case Op::Range: {
std::vector<int> result(get_arguments<1>(node)[0]->get<int>());
std::iota(result.begin(), result.end(), 0);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::move(result));
} break;
case Op::Round: {
- auto args = get_arguments<2>(node);
- int precision = args[1]->get<int>();
- double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ const auto args = get_arguments<2>(node);
+ const int precision = args[1]->get<int>();
+ const double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
+ if (precision == 0) {
+ make_result(int(result));
+ } else {
+ make_result(result);
+ }
} break;
case Op::Sort: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
+ auto result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
std::sort(result_ptr->begin(), result_ptr->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ data_tmp_stack.push_back(result_ptr);
+ data_eval_stack.push(result_ptr.get());
} break;
case Op::Upper: {
std::string result = get_arguments<1>(node)[0]->get<std::string>();
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(std::move(result));
} break;
case Op::IsBoolean: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_boolean());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_boolean());
} break;
case Op::IsNumber: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_number());
} break;
case Op::IsInteger: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_integer());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_number_integer());
} break;
case Op::IsFloat: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_float());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_number_float());
} break;
case Op::IsObject: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_object());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_object());
} break;
case Op::IsArray: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_array());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_array());
} break;
case Op::IsString: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_string());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ make_result(get_arguments<1>(node)[0]->is_string());
} break;
case Op::Callback: {
- auto args = get_argument_vector(node.number_args, node);
- result_ptr = std::make_shared<json>(node.callback(args));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
+ auto args = get_argument_vector(node);
+ make_result(node.callback(args));
+ } break;
+ case Op::Super: {
+ const auto args = get_argument_vector(node);
+ const size_t old_level = current_level;
+ const size_t level_diff = (args.size() == 1) ? args[0]->get<int>() : 1;
+ const size_t level = current_level + level_diff;
+
+ if (block_statement_stack.empty()) {
+ throw_renderer_error("super() call is not within a block", node);
+ }
+
+ if (level < 1 || level > template_stack.size() - 1) {
+ throw_renderer_error("level of super() call does not match parent templates (between 1 and " + std::to_string(template_stack.size() - 1) + ")", node);
+ }
+
+ const auto current_block_statement = block_statement_stack.back();
+ const Template* new_template = template_stack.at(level);
+ const Template* old_template = current_template;
+ const auto block_it = new_template->block_storage.find(current_block_statement->name);
+ if (block_it != new_template->block_storage.end()) {
+ current_template = new_template;
+ current_level = level;
+ block_it->second->block.accept(*this);
+ current_level = old_level;
+ current_template = old_template;
+ } else {
+ throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node);
+ }
+ make_result(nullptr);
+ } break;
+ case Op::Join: {
+ const auto args = get_arguments<2>(node);
+ const auto separator = args[1]->get<std::string>();
+ std::ostringstream os;
+ std::string sep;
+ for (const auto& value : *args[0]) {
+ os << sep;
+ if (value.is_string()) {
+ os << value.get<std::string>(); // otherwise the value is surrounded with ""
+ } else {
+ os << value;
+ }
+ sep = separator;
+ }
+ make_result(os.str());
} break;
case Op::ParenLeft:
case Op::ParenRight:
@@ -3803,15 +2552,15 @@ class Renderer : public NodeVisitor {
}
void visit(const ExpressionListNode& node) {
- print_json(eval_expression_list(node));
+ print_data(eval_expression_list(node));
}
- void visit(const StatementNode&) { }
+ void visit(const StatementNode&) {}
- void visit(const ForStatementNode&) { }
+ void visit(const ForStatementNode&) {}
void visit(const ForArrayStatementNode& node) {
- auto result = eval_expression_list(node.condition);
+ const auto result = eval_expression_list(node.condition);
if (!result->is_array()) {
throw_renderer_error("object must be an array", node);
}
@@ -3825,7 +2574,7 @@ class Renderer : public NodeVisitor {
(*current_loop_data)["is_first"] = true;
(*current_loop_data)["is_last"] = (result->size() <= 1);
for (auto it = result->begin(); it != result->end(); ++it) {
- json_additional_data[static_cast<std::string>(node.value)] = *it;
+ additional_data[static_cast<std::string>(node.value)] = *it;
(*current_loop_data)["index"] = index;
(*current_loop_data)["index1"] = index + 1;
@@ -3840,17 +2589,17 @@ class Renderer : public NodeVisitor {
++index;
}
- json_additional_data[static_cast<std::string>(node.value)].clear();
+ additional_data[static_cast<std::string>(node.value)].clear();
if (!(*current_loop_data)["parent"].empty()) {
- auto tmp = (*current_loop_data)["parent"];
+ const auto tmp = (*current_loop_data)["parent"];
*current_loop_data = std::move(tmp);
} else {
- current_loop_data = &json_additional_data["loop"];
+ current_loop_data = &additional_data["loop"];
}
}
void visit(const ForObjectStatementNode& node) {
- auto result = eval_expression_list(node.condition);
+ const auto result = eval_expression_list(node.condition);
if (!result->is_object()) {
throw_renderer_error("object must be an object", node);
}
@@ -3863,8 +2612,8 @@ class Renderer : public NodeVisitor {
(*current_loop_data)["is_first"] = true;
(*current_loop_data)["is_last"] = (result->size() <= 1);
for (auto it = result->begin(); it != result->end(); ++it) {
- json_additional_data[static_cast<std::string>(node.key)] = it.key();
- json_additional_data[static_cast<std::string>(node.value)] = it.value();
+ additional_data[static_cast<std::string>(node.key)] = it.key();
+ additional_data[static_cast<std::string>(node.value)] = it.value();
(*current_loop_data)["index"] = index;
(*current_loop_data)["index1"] = index + 1;
@@ -3879,17 +2628,17 @@ class Renderer : public NodeVisitor {
++index;
}
- json_additional_data[static_cast<std::string>(node.key)].clear();
- json_additional_data[static_cast<std::string>(node.value)].clear();
+ additional_data[static_cast<std::string>(node.key)].clear();
+ additional_data[static_cast<std::string>(node.value)].clear();
if (!(*current_loop_data)["parent"].empty()) {
*current_loop_data = std::move((*current_loop_data)["parent"]);
} else {
- current_loop_data = &json_additional_data["loop"];
+ current_loop_data = &additional_data["loop"];
}
}
void visit(const IfStatementNode& node) {
- auto result = eval_expression_list(node.condition);
+ const auto result = eval_expression_list(node.condition);
if (truthy(result.get())) {
node.true_statement.accept(*this);
} else if (node.has_false_statement) {
@@ -3899,35 +2648,63 @@ class Renderer : public NodeVisitor {
void visit(const IncludeStatementNode& node) {
auto sub_renderer = Renderer(config, template_storage, function_storage);
- auto included_template_it = template_storage.find(node.file);
-
+ const auto included_template_it = template_storage.find(node.file);
if (included_template_it != template_storage.end()) {
- sub_renderer.render_to(*output_stream, included_template_it->second, *json_input, &json_additional_data);
+ sub_renderer.render_to(*output_stream, included_template_it->second, *data_input, &additional_data);
} else if (config.throw_at_missing_includes) {
throw_renderer_error("include '" + node.file + "' not found", node);
}
}
+ void visit(const ExtendsStatementNode& node) {
+ const auto included_template_it = template_storage.find(node.file);
+ if (included_template_it != template_storage.end()) {
+ const Template* parent_template = &included_template_it->second;
+ render_to(*output_stream, *parent_template, *data_input, &additional_data);
+ break_rendering = true;
+ } else if (config.throw_at_missing_includes) {
+ throw_renderer_error("extends '" + node.file + "' not found", node);
+ }
+ }
+
+ void visit(const BlockStatementNode& node) {
+ const size_t old_level = current_level;
+ current_level = 0;
+ current_template = template_stack.front();
+ const auto block_it = current_template->block_storage.find(node.name);
+ if (block_it != current_template->block_storage.end()) {
+ block_statement_stack.emplace_back(&node);
+ block_it->second->block.accept(*this);
+ block_statement_stack.pop_back();
+ }
+ current_level = old_level;
+ current_template = template_stack.back();
+ }
+
void visit(const SetStatementNode& node) {
- json_additional_data[node.key] = *eval_expression_list(node.expression);
+ std::string ptr = node.key;
+ replace_substring(ptr, ".", "/");
+ ptr = "/" + ptr;
+ additional_data[json::json_pointer(ptr)] = *eval_expression_list(node.expression);
}
public:
- Renderer(const RenderConfig& config, const TemplateStorage &template_storage, const FunctionStorage &function_storage)
- : config(config), template_storage(template_storage), function_storage(function_storage) { }
+ Renderer(const RenderConfig& config, const TemplateStorage& template_storage, const FunctionStorage& function_storage)
+ : config(config), template_storage(template_storage), function_storage(function_storage) {}
- void render_to(std::ostream &os, const Template &tmpl, const json &data, json *loop_data = nullptr) {
+ void render_to(std::ostream& os, const Template& tmpl, const json& data, json* loop_data = nullptr) {
output_stream = &os;
current_template = &tmpl;
- json_input = &data;
+ data_input = &data;
if (loop_data) {
- json_additional_data = *loop_data;
- current_loop_data = &json_additional_data["loop"];
+ additional_data = *loop_data;
+ current_loop_data = &additional_data["loop"];
}
+ template_stack.emplace_back(current_template);
current_template->root.accept(*this);
- json_tmp_stack.clear();
+ data_tmp_stack.clear();
}
};
@@ -3935,8 +2712,6 @@ public:
#endif // INCLUDE_INJA_RENDERER_HPP_
-// #include "string_view.hpp"
-
// #include "template.hpp"
// #include "utils.hpp"
@@ -3944,8 +2719,6 @@ public:
namespace inja {
-using json = nlohmann::json;
-
/*!
* \brief Class for changing the configuration.
*/
@@ -3961,15 +2734,14 @@ class Environment {
TemplateStorage template_storage;
public:
- Environment() : Environment("") {}
+ Environment(): Environment("") {}
- explicit Environment(const std::string &global_path) : input_path(global_path), output_path(global_path) {}
+ explicit Environment(const std::string& global_path): input_path(global_path), output_path(global_path) {}
- Environment(const std::string &input_path, const std::string &output_path)
- : input_path(input_path), output_path(output_path) {}
+ Environment(const std::string& input_path, const std::string& output_path): input_path(input_path), output_path(output_path) {}
/// Sets the opener and closer for template statements
- void set_statement(const std::string &open, const std::string &close) {
+ void set_statement(const std::string& open, const std::string& close) {
lexer_config.statement_open = open;
lexer_config.statement_open_no_lstrip = open + "+";
lexer_config.statement_open_force_lstrip = open + "-";
@@ -3979,13 +2751,13 @@ public:
}
/// Sets the opener for template line statements
- void set_line_statement(const std::string &open) {
+ void set_line_statement(const std::string& open) {
lexer_config.line_statement = open;
lexer_config.update_open_chars();
}
/// Sets the opener and closer for template expressions
- void set_expression(const std::string &open, const std::string &close) {
+ void set_expression(const std::string& open, const std::string& close) {
lexer_config.expression_open = open;
lexer_config.expression_open_force_lstrip = open + "-";
lexer_config.expression_close = close;
@@ -3994,9 +2766,11 @@ public:
}
/// Sets the opener and closer for template comments
- void set_comment(const std::string &open, const std::string &close) {
+ void set_comment(const std::string& open, const std::string& close) {
lexer_config.comment_open = open;
+ lexer_config.comment_open_force_lstrip = open + "-";
lexer_config.comment_close = close;
+ lexer_config.comment_close_force_rstrip = "-" + close;
lexer_config.update_open_chars();
}
@@ -4020,75 +2794,79 @@ public:
render_config.throw_at_missing_includes = will_throw;
}
- Template parse(nonstd::string_view input) {
+ Template parse(std::string_view input) {
Parser parser(parser_config, lexer_config, template_storage, function_storage);
return parser.parse(input);
}
- Template parse_template(const std::string &filename) {
+ Template parse_template(const std::string& filename) {
Parser parser(parser_config, lexer_config, template_storage, function_storage);
auto result = Template(parser.load_file(input_path + static_cast<std::string>(filename)));
parser.parse_into_template(result, input_path + static_cast<std::string>(filename));
return result;
}
- Template parse_file(const std::string &filename) {
+ Template parse_file(const std::string& filename) {
return parse_template(filename);
}
- std::string render(nonstd::string_view input, const json &data) { return render(parse(input), data); }
+ std::string render(std::string_view input, const json& data) {
+ return render(parse(input), data);
+ }
- std::string render(const Template &tmpl, const json &data) {
+ std::string render(const Template& tmpl, const json& data) {
std::stringstream os;
render_to(os, tmpl, data);
return os.str();
}
- std::string render_file(const std::string &filename, const json &data) {
+ std::string render_file(const std::string& filename, const json& data) {
return render(parse_template(filename), data);
}
- std::string render_file_with_json_file(const std::string &filename, const std::string &filename_data) {
+ std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) {
const json data = load_json(filename_data);
return render_file(filename, data);
}
- void write(const std::string &filename, const json &data, const std::string &filename_out) {
+ void write(const std::string& filename, const json& data, const std::string& filename_out) {
std::ofstream file(output_path + filename_out);
file << render_file(filename, data);
file.close();
}
- void write(const Template &temp, const json &data, const std::string &filename_out) {
+ void write(const Template& temp, const json& data, const std::string& filename_out) {
std::ofstream file(output_path + filename_out);
file << render(temp, data);
file.close();
}
- void write_with_json_file(const std::string &filename, const std::string &filename_data,
- const std::string &filename_out) {
+ void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
const json data = load_json(filename_data);
write(filename, data, filename_out);
}
- void write_with_json_file(const Template &temp, const std::string &filename_data, const std::string &filename_out) {
+ void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
const json data = load_json(filename_data);
write(temp, data, filename_out);
}
- std::ostream &render_to(std::ostream &os, const Template &tmpl, const json &data) {
+ std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data);
return os;
}
- std::string load_file(const std::string &filename) {
+ std::string load_file(const std::string& filename) {
Parser parser(parser_config, lexer_config, template_storage, function_storage);
return parser.load_file(input_path + filename);
}
- json load_json(const std::string &filename) {
+ json load_json(const std::string& filename) {
std::ifstream file;
- open_file_or_throw(input_path + filename, file);
+ file.open(input_path + filename);
+ if (file.fail()) {
+ INJA_THROW(FileError("failed accessing file at '" + input_path + filename + "'"));
+ }
json j;
file >> j;
return j;
@@ -4097,51 +2875,61 @@ public:
/*!
@brief Adds a variadic callback
*/
- void add_callback(const std::string &name, const CallbackFunction &callback) {
+ void add_callback(const std::string& name, const CallbackFunction& callback) {
add_callback(name, -1, callback);
}
/*!
@brief Adds a variadic void callback
*/
- void add_void_callback(const std::string &name, const VoidCallbackFunction &callback) {
+ void add_void_callback(const std::string& name, const VoidCallbackFunction& callback) {
add_void_callback(name, -1, callback);
}
/*!
@brief Adds a callback with given number or arguments
*/
- void add_callback(const std::string &name, int num_args, const CallbackFunction &callback) {
+ void add_callback(const std::string& name, int num_args, const CallbackFunction& callback) {
function_storage.add_callback(name, num_args, callback);
}
/*!
@brief Adds a void callback with given number or arguments
*/
- void add_void_callback(const std::string &name, int num_args, const VoidCallbackFunction &callback) {
- function_storage.add_callback(name, num_args, [callback](Arguments& args) { callback(args); return json(); });
+ void add_void_callback(const std::string& name, int num_args, const VoidCallbackFunction& callback) {
+ function_storage.add_callback(name, num_args, [callback](Arguments& args) {
+ callback(args);
+ return json();
+ });
}
/** Includes a template with a given name into the environment.
* Then, a template can be rendered in another template using the
* include "<name>" syntax.
*/
- void include_template(const std::string &name, const Template &tmpl) {
+ void include_template(const std::string& name, const Template& tmpl) {
template_storage[name] = tmpl;
}
+
+ /*!
+ @brief Sets a function that is called when an included file is not found
+ */
+ void set_include_callback(const std::function<Template(const std::string&, const std::string&)>& callback) {
+ parser_config.include_callback = callback;
+ }
};
/*!
@brief render with default settings to a string
*/
-inline std::string render(nonstd::string_view input, const json &data) {
+inline std::string render(std::string_view input, const json& data) {
return Environment().render(input, data);
}
/*!
@brief render with default settings to the given output stream
*/
-inline void render_to(std::ostream &os, nonstd::string_view input, const json &data) {
+inline void render_to(std::ostream& os, std::string_view input, const json& data) {
Environment env;
env.render_to(os, env.parse(input), data);
}
@@ -4156,8 +2944,6 @@ inline void render_to(std::ostream &os, nonstd::string_view input, const json &d
// #include "renderer.hpp"
-// #include "string_view.hpp"
-
// #include "template.hpp"
diff --git a/test/benchmark.cpp b/test/benchmark.cpp
index 571a3ed..92c696f 100644
--- a/test/benchmark.cpp
+++ b/test/benchmark.cpp
@@ -3,22 +3,24 @@
#include "hayai/hayai.hpp"
#include <inja/inja.hpp>
-using json = nlohmann::json;
-
inja::Environment env;
const std::string test_file_directory {"../test/data/benchmark/"};
-json small_data = env.load_json(test_file_directory + "small_data.json");
-json large_data = env.load_json(test_file_directory + "large_data.json");
+auto small_data = env.load_json(test_file_directory + "small_data.json");
+auto large_data = env.load_json(test_file_directory + "large_data.json");
std::string medium_template = env.load_file(test_file_directory + "medium_template.txt");
std::string large_template = env.load_file(test_file_directory + "large_template.txt");
-
-BENCHMARK(SmallDataMediumTemplate, render, 5, 30) { env.render(medium_template, small_data); }
-BENCHMARK(LargeDataMediumTemplate, render, 5, 15) { env.render(medium_template, large_data); }
-BENCHMARK(LargeDataLargeTemplate, render, 5, 5) { env.render(large_template, large_data); }
-
+BENCHMARK(SmallDataMediumTemplate, render, 5, 30) {
+ env.render(medium_template, small_data);
+}
+BENCHMARK(LargeDataMediumTemplate, render, 5, 15) {
+ env.render(medium_template, large_data);
+}
+BENCHMARK(LargeDataLargeTemplate, render, 5, 5) {
+ env.render(large_template, large_data);
+}
int main() {
hayai::ConsoleOutputter consoleOutputter;
diff --git a/test/data/html-extend/base.txt b/test/data/html-extend/base.txt
new file mode 100644
index 0000000..e301c7a
--- /dev/null
+++ b/test/data/html-extend/base.txt
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ {% block head -%}
+ <title>{% block title %}Default - {% endblock %}</title>
+ {%- endblock -%}
+</head>
+<body>
+ {% block body %}ignored{% endblock %}
+</body>
+</html>
diff --git a/test/data/html-extend/data.json b/test/data/html-extend/data.json
new file mode 100644
index 0000000..c5726a5
--- /dev/null
+++ b/test/data/html-extend/data.json
@@ -0,0 +1,12 @@
+{
+ "author": "Pantor",
+ "date": "23/12/2018",
+ "tags": [
+ "test",
+ "templates"
+ ],
+ "views": 123,
+ "title": "Inja works.",
+ "content": "Inja is the best and fastest template engine for C++. Period.",
+ "footer-text": "This is the footer."
+}
diff --git a/test/data/html-extend/inter.txt b/test/data/html-extend/inter.txt
new file mode 100644
index 0000000..d20d97b
--- /dev/null
+++ b/test/data/html-extend/inter.txt
@@ -0,0 +1,3 @@
+{% extends "base.txt" %}
+{% block title %}Inter {{ author }}{% endblock %}
+{% block body %}<main>Intermediate Content</main>{% endblock %} \ No newline at end of file
diff --git a/test/data/html-extend/result.txt b/test/data/html-extend/result.txt
new file mode 100644
index 0000000..6d99e29
--- /dev/null
+++ b/test/data/html-extend/result.txt
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Default - Inter Pantor: Inja works.</title>
+
+ <style type="text/css">.important { color: #336699; }</style>
+</head>
+<body>
+ <main>Inja is the best and fastest template engine for C++. Period.</main>
+</body>
+</html>
diff --git a/test/data/html-extend/template.txt b/test/data/html-extend/template.txt
new file mode 100644
index 0000000..18eeb5d
--- /dev/null
+++ b/test/data/html-extend/template.txt
@@ -0,0 +1,7 @@
+{% extends "inter.txt" %}
+{% block head -%}
+ {{ super(2) }}
+ <style type="text/css">.important { color: #336699; }</style>
+{%- endblock %}
+{% block title %}{{ super(2) }}{{ super() }}: {{ title }}{% endblock %}
+{% block body %}<main>{{ content }}</main>{% endblock %}
diff --git a/test/data/include-both.txt b/test/data/include-both.txt
new file mode 100755
index 0000000..cbdc374
--- /dev/null
+++ b/test/data/include-both.txt
@@ -0,0 +1 @@
+{% include "simple.txt" %} - {% include "body" %} \ No newline at end of file
diff --git a/test/jinja.py b/test/jinja.py
new file mode 100644
index 0000000..94bc24c
--- /dev/null
+++ b/test/jinja.py
@@ -0,0 +1,11 @@
+from jinja2 import Environment, PackageLoader, select_autoescape
+
+
+if __name__ == '__main__':
+ env = Environment(
+ loader=PackageLoader("jinja"),
+ autoescape=select_autoescape()
+ )
+
+ template = env.get_template("example.txt")
+ print(template.render(name="Jeff"))
diff --git a/test/templates/example.txt b/test/templates/example.txt
new file mode 100644
index 0000000..655a3c9
--- /dev/null
+++ b/test/templates/example.txt
@@ -0,0 +1 @@
+Hello {{- name -}} ! \ No newline at end of file
diff --git a/test/test-files.cpp b/test/test-files.cpp
index 74d099a..aa01cff 100644
--- a/test/test-files.cpp
+++ b/test/test-files.cpp
@@ -2,10 +2,12 @@
TEST_CASE("loading") {
inja::Environment env;
- json data;
+ inja::json data;
data["name"] = "Jeff";
- SUBCASE("Files should be loaded") { CHECK(env.load_file(test_file_directory + "simple.txt") == "Hello {{ name }}."); }
+ SUBCASE("Files should be loaded") {
+ CHECK(env.load_file(test_file_directory + "simple.txt") == "Hello {{ name }}.");
+ }
SUBCASE("Files should be rendered") {
CHECK(env.render_file(test_file_directory + "simple.txt", data) == "Hello Jeff.");
@@ -27,10 +29,9 @@ TEST_CASE("loading") {
TEST_CASE("complete-files") {
inja::Environment env {test_file_directory};
- for (std::string test_name : {"simple-file", "nested", "nested-line", "html"}) {
+ for (std::string test_name : {"simple-file", "nested", "nested-line", "html", "html-extend"}) {
SUBCASE(test_name.c_str()) {
- CHECK(env.render_file_with_json_file(test_name + "/template.txt", test_name + "/data.json") ==
- env.load_file(test_name + "/result.txt"));
+ CHECK(env.render_file_with_json_file(test_name + "/template.txt", test_name + "/data.json") == env.load_file(test_name + "/result.txt"));
}
}
@@ -49,8 +50,7 @@ TEST_CASE("complete-files-whitespace-control") {
for (std::string test_name : {"nested-whitespace"}) {
SUBCASE(test_name.c_str()) {
- CHECK(env.render_file_with_json_file(test_name + "/template.txt", test_name + "/data.json") ==
- env.load_file(test_name + "/result.txt"));
+ CHECK(env.render_file_with_json_file(test_name + "/template.txt", test_name + "/data.json") == env.load_file(test_name + "/result.txt"));
}
}
}
@@ -58,7 +58,7 @@ TEST_CASE("complete-files-whitespace-control") {
TEST_CASE("global-path") {
inja::Environment env {test_file_directory, "./"};
inja::Environment env_result {"./"};
- json data;
+ inja::json data;
data["name"] = "Jeff";
SUBCASE("Files should be written") {
@@ -73,7 +73,20 @@ TEST_CASE("include-without-local-files") {
inja::Environment env {test_file_directory};
env.set_search_included_templates_in_files(false);
- SUBCASE("html") {
- CHECK_THROWS_WITH(env.render_file_with_json_file("html/template.txt", "html/data.json"), "[inja.exception.render_error] (at 3:14) include '../test/data/html/header.txt' not found");
- }
+ CHECK_THROWS_WITH(env.render_file_with_json_file("html/template.txt", "html/data.json"),
+ "[inja.exception.render_error] (at 3:14) include 'header.txt' not found");
+}
+
+TEST_CASE("include-in-memory-and-file-template") {
+ inja::Environment env {test_file_directory};
+
+ inja::json data;
+ data["name"] = "Jeff";
+
+ CHECK_THROWS_WITH(env.render_file("include-both.txt", data), "[inja.exception.file_error] failed accessing file at '../test/data/body'");
+
+ const auto parsed_body_template = env.parse("Bye {{ name }}.");
+ env.include_template("body", parsed_body_template);
+
+ CHECK(env.render_file("include-both.txt", data) == "Hello Jeff. - Bye Jeff.");
}
diff --git a/test/test-functions.cpp b/test/test-functions.cpp
index 4870205..667f553 100644
--- a/test/test-functions.cpp
+++ b/test/test-functions.cpp
@@ -3,7 +3,7 @@
TEST_CASE("functions") {
inja::Environment env;
- json data;
+ inja::json data;
data["name"] = "Peter";
data["city"] = "New York";
data["names"] = {"Jeff", "Seb", "Peter", "Tom"};
@@ -56,8 +56,8 @@ TEST_CASE("functions") {
SUBCASE("length") {
CHECK(env.render("{{ length(names) }}", data) == "4"); // Length of array
CHECK(env.render("{{ length(name) }}", data) == "5"); // Length of string
- // CHECK_THROWS_WITH( env.render("{{ length(5) }}", data), "[inja.exception.json_error]
- // [json.exception.type_error.302] type must be array, but is number" );
+ // CHECK_THROWS_WITH( env.render("{{ length(5) }}", data), "[inja.exception.json_error]
+ // [json.exception.type_error.302] type must be array, but is number" );
}
SUBCASE("sort") {
@@ -70,6 +70,8 @@ TEST_CASE("functions") {
SUBCASE("at") {
CHECK(env.render("{{ at(names, 0) }}", data) == "Jeff");
CHECK(env.render("{{ at(names, i) }}", data) == "Seb");
+ CHECK(env.render("{{ at(brother, \"name\") }}", data) == "Chris");
+ CHECK(env.render("{{ at(at(brother, \"daughters\"), 0) }}", data) == "Maria");
// CHECK(env.render("{{ at(names, 45) }}", data) == "Jeff");
}
@@ -86,7 +88,7 @@ TEST_CASE("functions") {
}
SUBCASE("round") {
- CHECK(env.render("{{ round(4, 0) }}", data) == "4.0");
+ CHECK(env.render("{{ round(4, 0) }}", data) == "4");
CHECK(env.render("{{ round(temperature, 2) }}", data) == "25.68");
// CHECK_THROWS_WITH( env.render("{{ round(name, 2) }}", data), "[inja.exception.json_error]
// [json.exception.type_error.302] type must be number, but is string" );
@@ -148,8 +150,7 @@ TEST_CASE("functions") {
CHECK(env.render("{{ default(name, \"nobody\") }}", data) == "Peter");
CHECK(env.render("{{ default(surname, \"nobody\") }}", data) == "nobody");
CHECK(env.render("{{ default(surname, \"{{ surname }}\") }}", data) == "{{ surname }}");
- CHECK_THROWS_WITH(env.render("{{ default(surname, lastname) }}", data),
- "[inja.exception.render_error] (at 1:21) variable 'lastname' not found");
+ CHECK_THROWS_WITH(env.render("{{ default(surname, lastname) }}", data), "[inja.exception.render_error] (at 1:21) variable 'lastname' not found");
}
SUBCASE("exists") {
@@ -166,10 +167,13 @@ TEST_CASE("functions") {
CHECK(env.render("{{ existsIn(brother, \"parents\") }}", data) == "false");
CHECK(env.render("{{ existsIn(brother, property) }}", data) == "true");
CHECK(env.render("{{ existsIn(brother, name) }}", data) == "false");
- CHECK_THROWS_WITH(env.render("{{ existsIn(sister, \"lastname\") }}", data),
- "[inja.exception.render_error] (at 1:13) variable 'sister' not found");
- CHECK_THROWS_WITH(env.render("{{ existsIn(brother, sister) }}", data),
- "[inja.exception.render_error] (at 1:22) variable 'sister' not found");
+ CHECK_THROWS_WITH(env.render("{{ existsIn(sister, \"lastname\") }}", data), "[inja.exception.render_error] (at 1:13) variable 'sister' not found");
+ CHECK_THROWS_WITH(env.render("{{ existsIn(brother, sister) }}", data), "[inja.exception.render_error] (at 1:22) variable 'sister' not found");
+ }
+
+ SUBCASE("join") {
+ CHECK(env.render("{{ join(names, \" | \") }}", data) == "Jeff | Seb | Peter | Tom");
+ CHECK(env.render("{{ join(vars, \", \") }}", data) == "2, 3, 4, 0, -1, -2, -3");
}
SUBCASE("isType") {
@@ -190,12 +194,22 @@ TEST_CASE("functions") {
}
}
+TEST_CASE("assignments") {
+ inja::Environment env;
+ inja::json data;
+ data["age"] = 28;
+
+ CHECK(env.render("{% set new_hour=23 %}{{ new_hour }}", data) == "23");
+ CHECK(env.render("{% set time.start=18 %}{{ time.start }}pm", data) == "18pm");
+ CHECK(env.render("{% set v1 = \"a\" %}{% set v2 = \"b\" %}{% set var = v1 + v2 %}{{ var }}", data) == "ab");
+}
+
TEST_CASE("callbacks") {
inja::Environment env;
- json data;
+ inja::json data;
data["age"] = 28;
- env.add_callback("double", 1, [](inja::Arguments &args) {
+ env.add_callback("double", 1, [](inja::Arguments& args) {
int number = args.at(0)->get<int>();
return 2 * number;
});
@@ -206,7 +220,7 @@ TEST_CASE("callbacks") {
});
std::string greet = "Hello";
- env.add_callback("double-greetings", 0, [greet](inja::Arguments args) { return greet + " " + greet + "!"; });
+ env.add_callback("double-greetings", 0, [greet](inja::Arguments) { return greet + " " + greet + "!"; });
env.add_callback("multiply", 2, [](inja::Arguments args) {
double number1 = args.at(0)->get<double>();
@@ -226,11 +240,11 @@ TEST_CASE("callbacks") {
return number1.length();
});
- env.add_void_callback("log", 1, [](inja::Arguments args) {
- int a = 2;
+ env.add_void_callback("log", 1, [](inja::Arguments) {
+
});
- env.add_callback("multiply", 0, [](inja::Arguments args) { return 1.0; });
+ env.add_callback("multiply", 0, [](inja::Arguments) { return 1.0; });
CHECK(env.render("{{ double(age) }}", data) == "56");
CHECK(env.render("{{ half(age) }}", data) == "14");
@@ -245,7 +259,7 @@ TEST_CASE("callbacks") {
SUBCASE("Variadic") {
env.add_callback("argmax", [](inja::Arguments& args) {
- auto result = std::max_element(args.begin(), args.end(), [](const json* a, const json* b) { return *a < *b;});
+ auto result = std::max_element(args.begin(), args.end(), [](const inja::json* a, const inja::json* b) { return *a < *b; });
return std::distance(args.begin(), result);
});
@@ -256,7 +270,7 @@ TEST_CASE("callbacks") {
TEST_CASE("combinations") {
inja::Environment env;
- json data;
+ inja::json data;
data["name"] = "Peter";
data["city"] = "Brunswick";
data["age"] = 29;
@@ -282,4 +296,5 @@ TEST_CASE("combinations") {
CHECK(env.render("{{ not true }}", data) == "false");
CHECK(env.render("{{ not (true) }}", data) == "false");
CHECK(env.render("{{ true or (true or true) }}", data) == "true");
+ CHECK(env.render("{{ at(list_of_objects, 1).b }}", data) == "3");
}
diff --git a/test/test-renderer.cpp b/test/test-renderer.cpp
index fe38738..2089791 100644
--- a/test/test-renderer.cpp
+++ b/test/test-renderer.cpp
@@ -2,7 +2,7 @@
TEST_CASE("types") {
inja::Environment env;
- json data;
+ inja::json data;
data["name"] = "Peter";
data["city"] = "Brunswick";
data["age"] = 29;
@@ -50,31 +50,26 @@ TEST_CASE("types") {
SUBCASE("loops") {
CHECK(env.render("{% for name in names %}a{% endfor %}", data) == "aa");
CHECK(env.render("Hello {% for name in names %}{{ name }} {% endfor %}!", data) == "Hello Jeff Seb !");
- CHECK(env.render("Hello {% for name in names %}{{ loop.index }}: {{ name }}, {% endfor %}!", data) ==
- "Hello 0: Jeff, 1: Seb, !");
+ CHECK(env.render("Hello {% for name in names %}{{ loop.index }}: {{ name }}, {% endfor %}!", data) == "Hello 0: Jeff, 1: Seb, !");
CHECK(env.render("{% for type, name in relatives %}{{ loop.index1 }}: {{ type }}: {{ name }}{% if loop.is_last == "
"false %}, {% endif %}{% endfor %}",
data) == "1: brother: Chris, 2: mother: Maria, 3: sister: Jenny");
CHECK(env.render("{% for v in vars %}{% if v > 0 %}+{% endif %}{% endfor %}", data) == "+++");
- CHECK(env.render(
- "{% for name in names %}{{ loop.index }}: {{ name }}{% if not loop.is_last %}, {% endif %}{% endfor %}!",
- data) == "0: Jeff, 1: Seb!");
+ CHECK(env.render("{% for name in names %}{{ loop.index }}: {{ name }}{% if not loop.is_last %}, {% endif %}{% endfor %}!", data) == "0: Jeff, 1: Seb!");
CHECK(env.render("{% for name in names %}{{ loop.index }}: {{ name }}{% if loop.is_last == false %}, {% endif %}{% "
"endfor %}!",
data) == "0: Jeff, 1: Seb!");
CHECK(env.render("{% for name in [] %}a{% endfor %}", data) == "");
- CHECK_THROWS_WITH(env.render("{% for name ins names %}a{% endfor %}", data),
- "[inja.exception.parser_error] (at 1:13) expected 'in', got 'ins'");
- CHECK_THROWS_WITH(env.render("{% for name in empty_loop %}a{% endfor %}", data),
- "[inja.exception.render_error] (at 1:16) variable 'empty_loop' not found");
+ CHECK_THROWS_WITH(env.render("{% for name ins names %}a{% endfor %}", data), "[inja.exception.parser_error] (at 1:13) expected 'in', got 'ins'");
+ CHECK_THROWS_WITH(env.render("{% for name in empty_loop %}a{% endfor %}", data), "[inja.exception.render_error] (at 1:16) variable 'empty_loop' not found");
// CHECK_THROWS_WITH( env.render("{% for name in relatives %}{{ name }}{% endfor %}", data),
// "[inja.exception.json_error] [json.exception.type_error.302] type must be array, but is object" );
}
SUBCASE("nested loops") {
- auto ldata = json::parse(R""""(
+ auto ldata = inja::json::parse(R""""(
{ "outer" : [
{ "inner" : [
{ "in2" : [ 1, 2 ] },
@@ -112,12 +107,10 @@ TEST_CASE("types") {
CHECK(env.render("{% if age >= 30 %}Right{% else %}Wrong{% endif %}", data) == "Wrong");
CHECK(env.render("{% if age in [28, 29, 30] %}True{% endif %}", data) == "True");
CHECK(env.render("{% if age == 28 %}28{% else if age == 29 %}29{% endif %}", data) == "29");
- CHECK(env.render("{% if age == 26 %}26{% else if age == 27 %}27{% else if age == 28 %}28{% else %}29{% endif %}",
- data) == "29");
+ CHECK(env.render("{% if age == 26 %}26{% else if age == 27 %}27{% else if age == 28 %}28{% else %}29{% endif %}", data) == "29");
CHECK(env.render("{% if age == 25 %}+{% endif %}{% if age == 29 %}+{% else %}-{% endif %}", data) == "+");
- CHECK_THROWS_WITH(env.render("{% if is_happy %}{% if is_happy %}{% endif %}", data),
- "[inja.exception.parser_error] (at 1:46) unmatched if");
+ CHECK_THROWS_WITH(env.render("{% if is_happy %}{% if is_happy %}{% endif %}", data), "[inja.exception.parser_error] (at 1:46) unmatched if");
CHECK_THROWS_WITH(env.render("{% if is_happy %}{% else if is_happy %}{% end if %}", data),
"[inja.exception.parser_error] (at 1:43) expected statement, got 'end'");
}
@@ -125,8 +118,18 @@ TEST_CASE("types") {
SUBCASE("set statements") {
CHECK(env.render("{% set predefined=true %}{% if predefined %}a{% endif %}", data) == "a");
CHECK(env.render("{% set predefined=false %}{% if predefined %}a{% endif %}", data) == "");
- CHECK_THROWS_WITH(env.render("{% if predefined %}{% endif %}", data),
- "[inja.exception.render_error] (at 1:7) variable 'predefined' not found");
+ CHECK(env.render("{% set age=30 %}{{age}}", data) == "30");
+ CHECK(env.render("{% set predefined.value=1 %}{% if existsIn(predefined, \"value\") %}{{predefined.value}}{% endif %}", data) == "1");
+ CHECK(env.render("{% set brother.name=\"Bob\" %}{{brother.name}}", data) == "Bob");
+ CHECK_THROWS_WITH(env.render("{% if predefined %}{% endif %}", data), "[inja.exception.render_error] (at 1:7) variable 'predefined' not found");
+ CHECK(env.render("{{age}}", data) == "29");
+ CHECK(env.render("{{brother.name}}", data) == "Chris");
+ }
+
+ SUBCASE("short circuit evaluation") {
+ CHECK(env.render("{% if 0 and undefined %}do{% else %}nothing{% endif %}", data) == "nothing");
+ CHECK_THROWS_WITH(env.render("{% if 1 and undefined %}do{% else %}nothing{% endif %}", data),
+ "[inja.exception.render_error] (at 1:13) variable 'undefined' not found");
}
SUBCASE("line statements") {
@@ -147,7 +150,7 @@ Yeah!
}
TEST_CASE("templates") {
- json data;
+ inja::json data;
data["name"] = "Peter";
data["city"] = "Brunswick";
data["is_happy"] = true;
@@ -170,19 +173,37 @@ TEST_CASE("templates") {
inja::Template t2 = env.parse("{% include \"greeting\" %}!");
CHECK(env.render(t2, data) == "Hello Peter!");
- CHECK_THROWS_WITH(env.parse("{% include \"does-not-exist\" %}!"),
- "[inja.exception.file_error] failed accessing file at 'does-not-exist'");
+ CHECK_THROWS_WITH(env.parse("{% include \"does-not-exist\" %}!"), "[inja.exception.file_error] failed accessing file at 'does-not-exist'");
+
+ CHECK_THROWS_WITH(env.parse("{% include does-not-exist %}!"), "[inja.exception.parser_error] (at 1:12) expected string, got 'does-not-exist'");
+ }
+
+ SUBCASE("include-callback") {
+ inja::Environment env;
+
+ CHECK_THROWS_WITH(env.parse("{% include \"does-not-exist\" %}!"), "[inja.exception.file_error] failed accessing file at 'does-not-exist'");
+
+ env.set_search_included_templates_in_files(false);
+ env.set_include_callback([&env](const std::string&, const std::string&) { return env.parse("Hello {{ name }}"); });
+
+ inja::Template t1 = env.parse("{% include \"greeting\" %}!");
+ CHECK(env.render(t1, data) == "Hello Peter!");
+
+ env.set_search_included_templates_in_files(true);
+ env.set_include_callback([&env](const std::string&, const std::string& name) { return env.parse("Bye " + name); });
+
+ inja::Template t2 = env.parse("{% include \"Jeff\" %}!");
+ CHECK(env.render(t2, data) == "Bye Jeff!");
}
SUBCASE("include-in-loop") {
- json loop_data;
- loop_data["cities"] = json::array({{{"name", "Munich"}}, {{"name", "New York"}}});
+ inja::json loop_data;
+ loop_data["cities"] = inja::json::array({{{"name", "Munich"}}, {{"name", "New York"}}});
inja::Environment env;
env.include_template("city.tpl", env.parse("{{ loop.index }}:{{ city.name }};"));
- CHECK(env.render("{% for city in cities %}{% include \"city.tpl\" %}{% endfor %}", loop_data) ==
- "0:Munich;1:New York;");
+ CHECK(env.render("{% for city in cities %}{% include \"city.tpl\" %}{% endfor %}", loop_data) == "0:Munich;1:New York;");
}
SUBCASE("count variables") {
@@ -204,32 +225,38 @@ TEST_CASE("templates") {
CHECK(env.render("Test\n {%- if is_happy %}{{ name }}{% endif %} ", data) == "Test\nPeter ");
CHECK(env.render(" {%+ if is_happy %}{{ name }}{% endif %}", data) == " Peter");
CHECK(env.render(" {%- if is_happy %}{{ name }}{% endif -%} \n ", data) == "Peter");
-
+
CHECK(env.render(" {{- name -}} \n ", data) == "Peter");
CHECK(env.render("Test\n {{- name }} ", data) == "Test\nPeter ");
CHECK(env.render(" {{ name }}\n ", data) == " Peter\n ");
+ CHECK(env.render("{{ name }}{# name -#} !", data) == "Peter!");
+ CHECK(env.render(" {#- name -#} !", data) == "!");
// Nothing will be stripped if there are other characters before the start of the block.
CHECK(env.render(". {%- if is_happy %}{{ name }}{% endif -%}\n", data) == ". Peter");
+ CHECK(env.render(". {#- comment -#}\n.", data) == ". .");
env.set_lstrip_blocks(true);
+ CHECK(env.render("Hello {{ name }}!", data) == "Hello Peter!");
CHECK(env.render(" {% if is_happy %}{{ name }}{% endif %}", data) == "Peter");
CHECK(env.render(" {% if is_happy %}{{ name }}{% endif %} ", data) == "Peter ");
CHECK(env.render(" {% if is_happy %}{{ name }}{% endif -%} ", data) == "Peter");
CHECK(env.render(" {%+ if is_happy %}{{ name }}{% endif %}", data) == " Peter");
CHECK(env.render("\n {%+ if is_happy %}{{ name }}{% endif -%} ", data) == "\n Peter");
CHECK(env.render("{% if is_happy %}{{ name }}{% endif %}\n", data) == "Peter\n");
+ CHECK(env.render(" {# comment #}", data) == "");
env.set_trim_blocks(true);
CHECK(env.render("{% if is_happy %}{{ name }}{% endif %}", data) == "Peter");
CHECK(env.render("{% if is_happy %}{{ name }}{% endif %}\n", data) == "Peter");
CHECK(env.render("{% if is_happy %}{{ name }}{% endif %} \n.", data) == "Peter.");
CHECK(env.render("{%- if is_happy %}{{ name }}{% endif -%} \n.", data) == "Peter.");
+ CHECK(env.render(" {# comment #} \n.", data) == ".");
}
}
TEST_CASE("other syntax") {
- json data;
+ inja::json data;
data["name"] = "Peter";
data["city"] = "Brunswick";
data["age"] = 29;
diff --git a/test/test-units.cpp b/test/test-units.cpp
index 95862f2..bbef190 100644
--- a/test/test-units.cpp
+++ b/test/test-units.cpp
@@ -27,7 +27,7 @@ Try this
TEST_CASE("copy environment") {
inja::Environment env;
- env.add_callback("double", 1, [](inja::Arguments &args) {
+ env.add_callback("double", 1, [](inja::Arguments& args) {
int number = args.at(0)->get<int>();
return 2 * number;
});
@@ -36,16 +36,16 @@ TEST_CASE("copy environment") {
env.include_template("tpl", t1);
std::string test_tpl = "{% include \"tpl\" %}";
- REQUIRE(env.render(test_tpl, json()) == "4");
+ REQUIRE(env.render(test_tpl, inja::json()) == "4");
inja::Environment copy(env);
- CHECK(copy.render(test_tpl, json()) == "4");
+ CHECK(copy.render(test_tpl, inja::json()) == "4");
// overwrite template in source env
inja::Template t2 = env.parse("{{ double(4) }}");
env.include_template("tpl", t2);
- REQUIRE(env.render(test_tpl, json()) == "8");
+ REQUIRE(env.render(test_tpl, inja::json()) == "8");
// template is unchanged in copy
- CHECK(copy.render(test_tpl, json()) == "4");
+ CHECK(copy.render(test_tpl, inja::json()) == "4");
}
diff --git a/test/test.cpp b/test/test.cpp
index 50161be..7645ade 100644
--- a/test/test.cpp
+++ b/test/test.cpp
@@ -5,8 +5,6 @@
#include "doctest/doctest.h"
#include "inja/inja.hpp"
-using json = nlohmann::json;
-
const std::string test_file_directory {"../test/data/"};
#include "test-files.cpp"
diff --git a/third_party/include/doctest/LICENSE.txt b/third_party/include/doctest/LICENSE.txt
index 6117758..c027373 100644
--- a/third_party/include/doctest/LICENSE.txt
+++ b/third_party/include/doctest/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2016-2019 Viktor Kirilov
+Copyright (c) 2016-2021 Viktor Kirilov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/third_party/include/doctest/doctest.h b/third_party/include/doctest/doctest.h
index c6bcafe..880e5bf 100644
--- a/third_party/include/doctest/doctest.h
+++ b/third_party/include/doctest/doctest.h
@@ -4,7 +4,7 @@
//
// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
//
-// Copyright (c) 2016-2019 Viktor Kirilov
+// Copyright (c) 2016-2021 Viktor Kirilov
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
@@ -47,9 +47,9 @@
// =================================================================================================
#define DOCTEST_VERSION_MAJOR 2
-#define DOCTEST_VERSION_MINOR 3
-#define DOCTEST_VERSION_PATCH 7
-#define DOCTEST_VERSION_STR "2.3.7"
+#define DOCTEST_VERSION_MINOR 4
+#define DOCTEST_VERSION_PATCH 6
+#define DOCTEST_VERSION_STR "2.4.6"
#define DOCTEST_VERSION \
(DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH)
@@ -301,11 +301,23 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_NOINLINE __declspec(noinline)
#define DOCTEST_UNUSED
#define DOCTEST_ALIGNMENT(x)
-#else // MSVC
+#elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0)
+#define DOCTEST_NOINLINE
+#define DOCTEST_UNUSED
+#define DOCTEST_ALIGNMENT(x)
+#else
#define DOCTEST_NOINLINE __attribute__((noinline))
#define DOCTEST_UNUSED __attribute__((unused))
#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x)))
-#endif // MSVC
+#endif
+
+#ifndef DOCTEST_NORETURN
+#define DOCTEST_NORETURN [[noreturn]]
+#endif // DOCTEST_NORETURN
+
+#ifndef DOCTEST_NOEXCEPT
+#define DOCTEST_NOEXCEPT noexcept
+#endif // DOCTEST_NOEXCEPT
// =================================================================================================
// == FEATURE DETECTION END ========================================================================
@@ -342,13 +354,25 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum'
#define DOCTEST_GLOBAL_NO_WARNINGS(var) \
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-variable") \
- static int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp)
+ static const int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp)
#define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP
#ifndef DOCTEST_BREAK_INTO_DEBUGGER
// should probably take a look at https://github.com/scottt/debugbreak
-#ifdef DOCTEST_PLATFORM_MAC
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :)
+#ifdef DOCTEST_PLATFORM_LINUX
+#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+// Break at the location of the failing check if possible
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
+#else
+#include <signal.h>
+#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP)
+#endif
+#elif defined(DOCTEST_PLATFORM_MAC)
+#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
+#else
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT (hicpp-no-assembler)
+#endif
#elif DOCTEST_MSVC
#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak()
#elif defined(__MINGW32__)
@@ -357,7 +381,7 @@ extern "C" __declspec(dllimport) void __stdcall DebugBreak();
DOCTEST_GCC_SUPPRESS_WARNING_POP
#define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak()
#else // linux
-#define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0)
+#define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast<void>(0))
#endif // linux
#endif // DOCTEST_BREAK_INTO_DEBUGGER
@@ -367,6 +391,9 @@ DOCTEST_GCC_SUPPRESS_WARNING_POP
#endif // DOCTEST_CONFIG_USE_IOSFWD
#ifdef DOCTEST_CONFIG_USE_STD_HEADERS
+#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
#include <iosfwd>
#include <cstddef>
#include <ostream>
@@ -629,12 +656,14 @@ DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file);
struct DOCTEST_INTERFACE TestCaseData
{
- const char* m_file; // the file in which the test was registered
+ String m_file; // the file in which the test was registered (using String - see #350)
unsigned m_line; // the line where the test was registered
const char* m_name; // name of the test case
const char* m_test_suite; // the test suite in which the test was added
const char* m_description;
bool m_skip;
+ bool m_no_breaks;
+ bool m_no_output;
bool m_may_fail;
bool m_should_fail;
int m_expected_failures;
@@ -688,12 +717,18 @@ struct DOCTEST_INTERFACE IContextScope
virtual void stringify(std::ostream*) const = 0;
};
+namespace detail {
+ struct DOCTEST_INTERFACE TestCase;
+} // namespace detail
+
struct ContextOptions //!OCLINT too many fields
{
std::ostream* cout; // stdout stream - std::cout by default
std::ostream* cerr; // stderr stream - std::cerr by default
String binary_name; // the test binary name
+ const detail::TestCase* currentTest = nullptr;
+
// == parameters from the command line
String out; // output filename
String order_by; // how tests should be ordered
@@ -720,7 +755,9 @@ struct ContextOptions //!OCLINT too many fields
bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x):
bool no_path_in_filenames; // if the path to files should be removed from the output
bool no_line_numbers; // if source code line numbers should be omitted from the output
+ bool no_debug_output; // no output in the debug console when a debugger is attached
bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!!
+ bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!!
bool help; // to print the help
bool version; // to print the version
@@ -731,7 +768,6 @@ struct ContextOptions //!OCLINT too many fields
};
namespace detail {
-#if defined(DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || defined(DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS)
template <bool CONDITION, typename TYPE = void>
struct enable_if
{};
@@ -739,15 +775,45 @@ namespace detail {
template <typename TYPE>
struct enable_if<true, TYPE>
{ typedef TYPE type; };
-#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
// clang-format off
template<class T> struct remove_reference { typedef T type; };
template<class T> struct remove_reference<T&> { typedef T type; };
template<class T> struct remove_reference<T&&> { typedef T type; };
+ template<typename T, typename U = T&&> U declval(int);
+
+ template<typename T> T declval(long);
+
+ template<typename T> auto declval() DOCTEST_NOEXCEPT -> decltype(declval<T>(0)) ;
+
+ template<class T> struct is_lvalue_reference { const static bool value=false; };
+ template<class T> struct is_lvalue_reference<T&> { const static bool value=true; };
+
+ template <class T>
+ inline T&& forward(typename remove_reference<T>::type& t) DOCTEST_NOEXCEPT
+ {
+ return static_cast<T&&>(t);
+ }
+
+ template <class T>
+ inline T&& forward(typename remove_reference<T>::type&& t) DOCTEST_NOEXCEPT
+ {
+ static_assert(!is_lvalue_reference<T>::value,
+ "Can not forward an rvalue as an lvalue.");
+ return static_cast<T&&>(t);
+ }
+
template<class T> struct remove_const { typedef T type; };
template<class T> struct remove_const<const T> { typedef T type; };
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ template<class T> struct is_enum : public std::is_enum<T> {};
+ template<class T> struct underlying_type : public std::underlying_type<T> {};
+#else
+ // Use compiler intrinsics
+ template<class T> struct is_enum { constexpr static bool value = __is_enum(T); };
+ template<class T> struct underlying_type { typedef __underlying_type(T) type; };
+#endif
// clang-format on
template <typename T>
@@ -756,33 +822,23 @@ namespace detail {
{ static const bool value = false; };
namespace has_insertion_operator_impl {
- typedef char no;
- typedef char yes[2];
+ std::ostream &os();
+ template<class T>
+ DOCTEST_REF_WRAP(T) val();
- struct any_t
- {
- template <typename T>
- // cppcheck-suppress noExplicitConstructor
- any_t(const DOCTEST_REF_WRAP(T));
+ template<class, class = void>
+ struct check {
+ static constexpr bool value = false;
};
- yes& testStreamable(std::ostream&);
- no testStreamable(no);
-
- no operator<<(const std::ostream&, const any_t&);
-
- template <typename T>
- struct has_insertion_operator
- {
- static std::ostream& s;
- static const DOCTEST_REF_WRAP(T) t;
- static const bool value = sizeof(decltype(testStreamable(s << t))) == sizeof(yes);
+ template<class T>
+ struct check<T, decltype(os() << val<T>(), void())> {
+ static constexpr bool value = true;
};
} // namespace has_insertion_operator_impl
- template <typename T>
- struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T>
- {};
+ template<class T>
+ using has_insertion_operator = has_insertion_operator_impl::check<const T>;
DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num);
@@ -846,7 +902,7 @@ struct StringMaker<R C::*>
}
};
-template <typename T>
+template <typename T, typename detail::enable_if<!detail::is_enum<T>::value, bool>::type = true>
String toString(const DOCTEST_REF_WRAP(T) value) {
return StringMaker<T>::convert(value);
}
@@ -873,6 +929,12 @@ DOCTEST_INTERFACE String toString(int long long in);
DOCTEST_INTERFACE String toString(int long long unsigned in);
DOCTEST_INTERFACE String toString(std::nullptr_t in);
+template <typename T, typename detail::enable_if<detail::is_enum<T>::value, bool>::type = true>
+String toString(const DOCTEST_REF_WRAP(T) value) {
+ typedef typename detail::underlying_type<T>::type UT;
+ return toString(static_cast<UT>(value));
+}
+
#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183
DOCTEST_INTERFACE String toString(const std::string& in);
@@ -987,7 +1049,7 @@ namespace detail {
DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at);
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
- [[noreturn]]
+ DOCTEST_NORETURN
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
DOCTEST_INTERFACE void throwException();
@@ -1005,13 +1067,24 @@ namespace detail {
template <typename L, typename R>
String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op,
const DOCTEST_REF_WRAP(R) rhs) {
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
return toString(lhs) + op + toString(rhs);
}
+#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
+DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
+#endif
+
+// This will check if there is any way it could find a operator like member or friend and uses it.
+// If not it doesn't find the operator or if the operator at global scope is defined after
+// this template, the template won't be instantiated due to SFINAE. Once the template is not
+// instantiated it can look for global operator using normal conversions.
+#define SFINAE_OP(ret,op) decltype(doctest::detail::declval<L>() op doctest::detail::declval<R>(),static_cast<ret>(0))
+
#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \
template <typename R> \
- DOCTEST_NOINLINE Result operator op(const DOCTEST_REF_WRAP(R) rhs) { \
- bool res = op_macro(lhs, rhs); \
+ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \
+ bool res = op_macro(doctest::detail::forward<L>(lhs), doctest::detail::forward<R>(rhs)); \
if(m_at & assertType::is_false) \
res = !res; \
if(!res || doctest::getContextOptions()->success) \
@@ -1092,6 +1165,7 @@ namespace detail {
#define DOCTEST_COMPARISON_RETURN_TYPE bool
#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
#define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); }
@@ -1138,12 +1212,16 @@ namespace detail {
L lhs;
assertType::Enum m_at;
- explicit Expression_lhs(L in, assertType::Enum at)
- : lhs(in)
+ explicit Expression_lhs(L&& in, assertType::Enum at)
+ : lhs(doctest::detail::forward<L>(in))
, m_at(at) {}
DOCTEST_NOINLINE operator Result() {
- bool res = !!lhs;
+// this is needed only foc MSVC 2015:
+// https://ci.appveyor.com/project/onqtam/doctest/builds/38181202
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool
+ bool res = static_cast<bool>(lhs);
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional
res = !res;
@@ -1152,6 +1230,10 @@ namespace detail {
return Result(res);
}
+ /* This is required for user-defined conversions from Expression_lhs to L */
+ //operator L() const { return lhs; }
+ operator L() const { return lhs; }
+
// clang-format off
DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional
DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional
@@ -1192,6 +1274,10 @@ namespace detail {
#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
+DOCTEST_CLANG_SUPPRESS_WARNING_POP
+#endif
+
struct DOCTEST_INTERFACE ExpressionDecomposer
{
assertType::Enum m_at;
@@ -1203,8 +1289,8 @@ namespace detail {
// https://github.com/catchorg/Catch2/issues/870
// https://github.com/catchorg/Catch2/issues/565
template <typename L>
- Expression_lhs<const DOCTEST_REF_WRAP(L)> operator<<(const DOCTEST_REF_WRAP(L) operand) {
- return Expression_lhs<const DOCTEST_REF_WRAP(L)>(operand, m_at);
+ Expression_lhs<L> operator<<(L &&operand) {
+ return Expression_lhs<L>(doctest::detail::forward<L>(operand), m_at);
}
};
@@ -1213,6 +1299,8 @@ namespace detail {
const char* m_test_suite;
const char* m_description;
bool m_skip;
+ bool m_no_breaks;
+ bool m_no_output;
bool m_may_fail;
bool m_should_fail;
int m_expected_failures;
@@ -1284,12 +1372,12 @@ namespace detail {
template <class L, class R> struct RelationalComparator<n, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } };
// clang-format on
- DOCTEST_BINARY_RELATIONAL_OP(0, eq)
- DOCTEST_BINARY_RELATIONAL_OP(1, ne)
- DOCTEST_BINARY_RELATIONAL_OP(2, gt)
- DOCTEST_BINARY_RELATIONAL_OP(3, lt)
- DOCTEST_BINARY_RELATIONAL_OP(4, ge)
- DOCTEST_BINARY_RELATIONAL_OP(5, le)
+ DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq)
+ DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne)
+ DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt)
+ DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt)
+ DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge)
+ DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le)
struct DOCTEST_INTERFACE ResultBuilder : public AssertData
{
@@ -1416,9 +1504,9 @@ namespace detail {
} catch(T ex) { // NOLINT
res = m_translateFunction(ex); //!OCLINT parameter reassignment
return true;
- } catch(...) {} //!OCLINT - empty catch statement
-#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
- ((void)res); // to silence -Wunused-parameter
+ } catch(...) {} //!OCLINT - empty catch statement
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+ static_cast<void>(res); // to silence -Wunused-parameter
return false;
}
@@ -1491,7 +1579,7 @@ namespace detail {
template <typename L> class ContextScope : public ContextScopeBase
{
- const L &lambda_;
+ const L lambda_;
public:
explicit ContextScope(const L &lambda) : lambda_(lambda) {}
@@ -1511,12 +1599,24 @@ namespace detail {
MessageBuilder() = delete;
~MessageBuilder();
+ // the preferred way of chaining parameters for stringification
template <typename T>
- MessageBuilder& operator<<(const T& in) {
+ MessageBuilder& operator,(const T& in) {
toStream(m_stream, in);
return *this;
}
+ // kept here just for backwards-compatibility - the comma operator should be preferred now
+ template <typename T>
+ MessageBuilder& operator<<(const T& in) { return this->operator,(in); }
+
+ // the `,` operator has the lowest operator precedence - if `<<` is used by the user then
+ // the `,` operator will be called last which is not what we want and thus the `*` operator
+ // is used first (has higher operator precedence compared to `<<`) so that we guarantee that
+ // an operator of the MessageBuilder class is called first before the rest of the parameters
+ template <typename T>
+ MessageBuilder& operator*(const T& in) { return this->operator,(in); }
+
bool log();
void react();
};
@@ -1540,6 +1640,8 @@ namespace detail {
DOCTEST_DEFINE_DECORATOR(test_suite, const char*, "");
DOCTEST_DEFINE_DECORATOR(description, const char*, "");
DOCTEST_DEFINE_DECORATOR(skip, bool, true);
+DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true);
+DOCTEST_DEFINE_DECORATOR(no_output, bool, true);
DOCTEST_DEFINE_DECORATOR(timeout, double, 0);
DOCTEST_DEFINE_DECORATOR(may_fail, bool, true);
DOCTEST_DEFINE_DECORATOR(should_fail, bool, true);
@@ -1737,12 +1839,12 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
#ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
-#define DOCTEST_CAST_TO_VOID(x) \
+#define DOCTEST_CAST_TO_VOID(...) \
DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \
- static_cast<void>(x); \
+ static_cast<void>(__VA_ARGS__); \
DOCTEST_GCC_SUPPRESS_WARNING_POP
#else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
-#define DOCTEST_CAST_TO_VOID(x) x;
+#define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__;
#endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
// registers the test by initializing a dummy var with a function
@@ -1876,10 +1978,12 @@ int registerReporter(const char* name, int priority, bool isReporter) {
static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \
- static doctest::detail::TestSuite data; \
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \
+ static doctest::detail::TestSuite data{}; \
static bool inited = false; \
DOCTEST_MSVC_SUPPRESS_WARNING_POP \
DOCTEST_CLANG_SUPPRESS_WARNING_POP \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP \
if(!inited) { \
data* decorators; \
inited = true; \
@@ -1932,38 +2036,36 @@ int registerReporter(const char* name, int priority, bool isReporter) {
DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_)
// for logging
-#define DOCTEST_INFO(expression) \
+#define DOCTEST_INFO(...) \
DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \
- DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), expression)
+ __VA_ARGS__)
-#define DOCTEST_INFO_IMPL(lambda_name, mb_name, s_name, expression) \
- DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4626) \
- auto lambda_name = [&](std::ostream* s_name) { \
+#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \
+ auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \
+ [&](std::ostream* s_name) { \
doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \
mb_name.m_stream = s_name; \
- mb_name << expression; \
- }; \
- DOCTEST_MSVC_SUPPRESS_WARNING_POP \
- auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope(lambda_name)
+ mb_name * __VA_ARGS__; \
+ })
-#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x)
+#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x)
-#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, x) \
+#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \
do { \
doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \
- mb << x; \
+ mb * __VA_ARGS__; \
DOCTEST_ASSERT_LOG_AND_REACT(mb); \
- } while((void)0, 0)
+ } while(false)
// clang-format off
-#define DOCTEST_ADD_MESSAGE_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x)
-#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x)
-#define DOCTEST_ADD_FAIL_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x)
+#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__)
// clang-format on
-#define DOCTEST_MESSAGE(x) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, x)
-#define DOCTEST_FAIL_CHECK(x) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, x)
-#define DOCTEST_FAIL(x) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, x)
+#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__)
+#define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__)
+#define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__)
#define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility.
@@ -1982,7 +2084,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \
do { \
DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \
- } while((void)0, 0)
+ } while(false)
#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
@@ -2006,12 +2108,12 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__)
// clang-format off
-#define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while((void)0, 0)
-#define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while((void)0, 0)
-#define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while((void)0, 0)
-#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while((void)0, 0)
-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while((void)0, 0)
-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while((void)0, 0)
+#define DOCTEST_WARN_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while(false)
+#define DOCTEST_CHECK_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while(false)
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while(false)
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while(false)
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while(false)
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while(false)
// clang-format on
#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \
@@ -2021,73 +2123,73 @@ int registerReporter(const char* name, int priority, bool isReporter) {
__LINE__, #expr, #__VA_ARGS__, message); \
try { \
DOCTEST_CAST_TO_VOID(expr) \
- } catch(const doctest::detail::remove_const< \
- doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \
+ } catch(const typename doctest::detail::remove_const< \
+ typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \
_DOCTEST_RB.translateException(); \
_DOCTEST_RB.m_threw_as = true; \
} catch(...) { _DOCTEST_RB.translateException(); } \
DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
} \
- } while((void)0, 0)
+ } while(false)
-#define DOCTEST_ASSERT_THROWS_WITH(expr, assert_type, ...) \
+#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \
do { \
if(!doctest::getContextOptions()->no_throw) { \
doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
- __LINE__, #expr, "", __VA_ARGS__); \
+ __LINE__, expr_str, "", __VA_ARGS__); \
try { \
DOCTEST_CAST_TO_VOID(expr) \
} catch(...) { _DOCTEST_RB.translateException(); } \
DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
} \
- } while((void)0, 0)
+ } while(false)
-#define DOCTEST_ASSERT_NOTHROW(expr, assert_type) \
+#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \
do { \
doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
- __LINE__, #expr); \
+ __LINE__, #__VA_ARGS__); \
try { \
- DOCTEST_CAST_TO_VOID(expr) \
+ DOCTEST_CAST_TO_VOID(__VA_ARGS__) \
} catch(...) { _DOCTEST_RB.translateException(); } \
DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
- } while((void)0, 0)
+ } while(false)
// clang-format off
-#define DOCTEST_WARN_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_WARN_THROWS, "")
-#define DOCTEST_CHECK_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_CHECK_THROWS, "")
-#define DOCTEST_REQUIRE_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_REQUIRE_THROWS, "")
+#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "")
+#define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "")
+#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "")
#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__)
#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__)
#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__)
-#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_WARN_THROWS_WITH, __VA_ARGS__)
-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_CHECK_THROWS_WITH, __VA_ARGS__)
-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__)
+#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__)
+#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__)
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__)
#define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__)
#define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__)
#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__)
-#define DOCTEST_WARN_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_WARN_NOTHROW)
-#define DOCTEST_CHECK_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_CHECK_NOTHROW)
-#define DOCTEST_REQUIRE_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_REQUIRE_NOTHROW)
-
-#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS(expr); } while((void)0, 0)
-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS(expr); } while((void)0, 0)
-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS(expr); } while((void)0, 0)
-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_AS(expr, ex); } while((void)0, 0)
-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_AS(expr, ex); } while((void)0, 0)
-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while((void)0, 0)
-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH(expr, with); } while((void)0, 0)
-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH(expr, with); } while((void)0, 0)
-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } while((void)0, 0)
-#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } while((void)0, 0)
-#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } while((void)0, 0)
-#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } while((void)0, 0)
-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_NOTHROW(expr); } while((void)0, 0)
-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_NOTHROW(expr); } while((void)0, 0)
-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_NOTHROW(expr); } while((void)0, 0)
+#define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__)
+#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__)
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } while(false)
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } while(false)
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } while(false)
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } while(false)
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } while(false)
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while(false)
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } while(false)
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } while(false)
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } while(false)
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } while(false)
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } while(false)
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } while(false)
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } while(false)
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } while(false)
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } while(false)
// clang-format on
#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
@@ -2100,7 +2202,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
_DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \
__VA_ARGS__)) \
DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
- } while((void)0, 0)
+ } while(false)
#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
do { \
@@ -2108,7 +2210,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
__LINE__, #__VA_ARGS__); \
DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(__VA_ARGS__)) \
DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \
- } while((void)0, 0)
+ } while(false)
#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
@@ -2184,37 +2286,37 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
-#define DOCTEST_WARN_THROWS(expr) ((void)0)
-#define DOCTEST_CHECK_THROWS(expr) ((void)0)
-#define DOCTEST_REQUIRE_THROWS(expr) ((void)0)
-#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0)
-#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0)
-#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0)
-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0)
-#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ((void)0)
-#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ((void)0)
-#define DOCTEST_WARN_NOTHROW(expr) ((void)0)
-#define DOCTEST_CHECK_NOTHROW(expr) ((void)0)
-#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0)
-
-#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0)
-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0)
-#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0)
-#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0)
-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0)
+#define DOCTEST_WARN_THROWS(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS(...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_NOTHROW(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_NOTHROW(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast<void>(0))
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
@@ -2305,86 +2407,86 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_REGISTER_REPORTER(name, priority, reporter)
#define DOCTEST_REGISTER_LISTENER(name, priority, reporter)
-#define DOCTEST_INFO(x) ((void)0)
-#define DOCTEST_CAPTURE(x) ((void)0)
-#define DOCTEST_ADD_MESSAGE_AT(file, line, x) ((void)0)
-#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) ((void)0)
-#define DOCTEST_ADD_FAIL_AT(file, line, x) ((void)0)
-#define DOCTEST_MESSAGE(x) ((void)0)
-#define DOCTEST_FAIL_CHECK(x) ((void)0)
-#define DOCTEST_FAIL(x) ((void)0)
-
-#define DOCTEST_WARN(...) ((void)0)
-#define DOCTEST_CHECK(...) ((void)0)
-#define DOCTEST_REQUIRE(...) ((void)0)
-#define DOCTEST_WARN_FALSE(...) ((void)0)
-#define DOCTEST_CHECK_FALSE(...) ((void)0)
-#define DOCTEST_REQUIRE_FALSE(...) ((void)0)
-
-#define DOCTEST_WARN_MESSAGE(cond, msg) ((void)0)
-#define DOCTEST_CHECK_MESSAGE(cond, msg) ((void)0)
-#define DOCTEST_REQUIRE_MESSAGE(cond, msg) ((void)0)
-#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) ((void)0)
-#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) ((void)0)
-#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) ((void)0)
-
-#define DOCTEST_WARN_THROWS(expr) ((void)0)
-#define DOCTEST_CHECK_THROWS(expr) ((void)0)
-#define DOCTEST_REQUIRE_THROWS(expr) ((void)0)
-#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0)
-#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0)
-#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0)
-#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0)
-#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ((void)0)
-#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ((void)0)
-#define DOCTEST_WARN_NOTHROW(expr) ((void)0)
-#define DOCTEST_CHECK_NOTHROW(expr) ((void)0)
-#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0)
-
-#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
-#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0)
-#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0)
-#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0)
-#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0)
-#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0)
-#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0)
-#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0)
-#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0)
-
-#define DOCTEST_WARN_EQ(...) ((void)0)
-#define DOCTEST_CHECK_EQ(...) ((void)0)
-#define DOCTEST_REQUIRE_EQ(...) ((void)0)
-#define DOCTEST_WARN_NE(...) ((void)0)
-#define DOCTEST_CHECK_NE(...) ((void)0)
-#define DOCTEST_REQUIRE_NE(...) ((void)0)
-#define DOCTEST_WARN_GT(...) ((void)0)
-#define DOCTEST_CHECK_GT(...) ((void)0)
-#define DOCTEST_REQUIRE_GT(...) ((void)0)
-#define DOCTEST_WARN_LT(...) ((void)0)
-#define DOCTEST_CHECK_LT(...) ((void)0)
-#define DOCTEST_REQUIRE_LT(...) ((void)0)
-#define DOCTEST_WARN_GE(...) ((void)0)
-#define DOCTEST_CHECK_GE(...) ((void)0)
-#define DOCTEST_REQUIRE_GE(...) ((void)0)
-#define DOCTEST_WARN_LE(...) ((void)0)
-#define DOCTEST_CHECK_LE(...) ((void)0)
-#define DOCTEST_REQUIRE_LE(...) ((void)0)
-
-#define DOCTEST_WARN_UNARY(...) ((void)0)
-#define DOCTEST_CHECK_UNARY(...) ((void)0)
-#define DOCTEST_REQUIRE_UNARY(...) ((void)0)
-#define DOCTEST_WARN_UNARY_FALSE(...) ((void)0)
-#define DOCTEST_CHECK_UNARY_FALSE(...) ((void)0)
-#define DOCTEST_REQUIRE_UNARY_FALSE(...) ((void)0)
+#define DOCTEST_INFO(...) (static_cast<void>(0))
+#define DOCTEST_CAPTURE(x) (static_cast<void>(0))
+#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast<void>(0))
+#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast<void>(0))
+#define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast<void>(0))
+#define DOCTEST_MESSAGE(...) (static_cast<void>(0))
+#define DOCTEST_FAIL_CHECK(...) (static_cast<void>(0))
+#define DOCTEST_FAIL(...) (static_cast<void>(0))
+
+#define DOCTEST_WARN(...) (static_cast<void>(0))
+#define DOCTEST_CHECK(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE(...) (static_cast<void>(0))
+#define DOCTEST_WARN_FALSE(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_FALSE(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_FALSE(...) (static_cast<void>(0))
+
+#define DOCTEST_WARN_MESSAGE(cond, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_MESSAGE(cond, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) (static_cast<void>(0))
+
+#define DOCTEST_WARN_THROWS(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS(...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_NOTHROW(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_NOTHROW(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast<void>(0))
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast<void>(0))
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast<void>(0))
+
+#define DOCTEST_WARN_EQ(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_EQ(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_EQ(...) (static_cast<void>(0))
+#define DOCTEST_WARN_NE(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_NE(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_NE(...) (static_cast<void>(0))
+#define DOCTEST_WARN_GT(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_GT(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_GT(...) (static_cast<void>(0))
+#define DOCTEST_WARN_LT(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_LT(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_LT(...) (static_cast<void>(0))
+#define DOCTEST_WARN_GE(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_GE(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_GE(...) (static_cast<void>(0))
+#define DOCTEST_WARN_LE(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_LE(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_LE(...) (static_cast<void>(0))
+
+#define DOCTEST_WARN_UNARY(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_UNARY(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_UNARY(...) (static_cast<void>(0))
+#define DOCTEST_WARN_UNARY_FALSE(...) (static_cast<void>(0))
+#define DOCTEST_CHECK_UNARY_FALSE(...) (static_cast<void>(0))
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) (static_cast<void>(0))
#endif // DOCTEST_CONFIG_DISABLE
@@ -2416,7 +2518,7 @@ int registerReporter(const char* name, int priority, bool isReporter) {
#define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE
#define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE
-#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INVOKE
+#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__)
// clang-format on
// BDD style macros
@@ -2436,138 +2538,138 @@ int registerReporter(const char* name, int priority, bool isReporter) {
// == SHORT VERSIONS OF THE MACROS
#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES)
-#define TEST_CASE DOCTEST_TEST_CASE
-#define TEST_CASE_CLASS DOCTEST_TEST_CASE_CLASS
-#define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE
-#define TYPE_TO_STRING DOCTEST_TYPE_TO_STRING
-#define TEST_CASE_TEMPLATE DOCTEST_TEST_CASE_TEMPLATE
-#define TEST_CASE_TEMPLATE_DEFINE DOCTEST_TEST_CASE_TEMPLATE_DEFINE
-#define TEST_CASE_TEMPLATE_INVOKE DOCTEST_TEST_CASE_TEMPLATE_INVOKE
-#define TEST_CASE_TEMPLATE_APPLY DOCTEST_TEST_CASE_TEMPLATE_APPLY
-#define SUBCASE DOCTEST_SUBCASE
-#define TEST_SUITE DOCTEST_TEST_SUITE
-#define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN
+#define TEST_CASE(name) DOCTEST_TEST_CASE(name)
+#define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name)
+#define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name)
+#define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__)
+#define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__)
+#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id)
+#define TEST_CASE_TEMPLATE_INVOKE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__)
+#define TEST_CASE_TEMPLATE_APPLY(id, ...) DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, __VA_ARGS__)
+#define SUBCASE(name) DOCTEST_SUBCASE(name)
+#define TEST_SUITE(decorators) DOCTEST_TEST_SUITE(decorators)
+#define TEST_SUITE_BEGIN(name) DOCTEST_TEST_SUITE_BEGIN(name)
#define TEST_SUITE_END DOCTEST_TEST_SUITE_END
-#define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR
-#define REGISTER_REPORTER DOCTEST_REGISTER_REPORTER
-#define REGISTER_LISTENER DOCTEST_REGISTER_LISTENER
-#define INFO DOCTEST_INFO
-#define CAPTURE DOCTEST_CAPTURE
-#define ADD_MESSAGE_AT DOCTEST_ADD_MESSAGE_AT
-#define ADD_FAIL_CHECK_AT DOCTEST_ADD_FAIL_CHECK_AT
-#define ADD_FAIL_AT DOCTEST_ADD_FAIL_AT
-#define MESSAGE DOCTEST_MESSAGE
-#define FAIL_CHECK DOCTEST_FAIL_CHECK
-#define FAIL DOCTEST_FAIL
-#define TO_LVALUE DOCTEST_TO_LVALUE
-
-#define WARN DOCTEST_WARN
-#define WARN_FALSE DOCTEST_WARN_FALSE
-#define WARN_THROWS DOCTEST_WARN_THROWS
-#define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS
-#define WARN_THROWS_WITH DOCTEST_WARN_THROWS_WITH
-#define WARN_THROWS_WITH_AS DOCTEST_WARN_THROWS_WITH_AS
-#define WARN_NOTHROW DOCTEST_WARN_NOTHROW
-#define CHECK DOCTEST_CHECK
-#define CHECK_FALSE DOCTEST_CHECK_FALSE
-#define CHECK_THROWS DOCTEST_CHECK_THROWS
-#define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS
-#define CHECK_THROWS_WITH DOCTEST_CHECK_THROWS_WITH
-#define CHECK_THROWS_WITH_AS DOCTEST_CHECK_THROWS_WITH_AS
-#define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW
-#define REQUIRE DOCTEST_REQUIRE
-#define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE
-#define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS
-#define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS
-#define REQUIRE_THROWS_WITH DOCTEST_REQUIRE_THROWS_WITH
-#define REQUIRE_THROWS_WITH_AS DOCTEST_REQUIRE_THROWS_WITH_AS
-#define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW
-
-#define WARN_MESSAGE DOCTEST_WARN_MESSAGE
-#define WARN_FALSE_MESSAGE DOCTEST_WARN_FALSE_MESSAGE
-#define WARN_THROWS_MESSAGE DOCTEST_WARN_THROWS_MESSAGE
-#define WARN_THROWS_AS_MESSAGE DOCTEST_WARN_THROWS_AS_MESSAGE
-#define WARN_THROWS_WITH_MESSAGE DOCTEST_WARN_THROWS_WITH_MESSAGE
-#define WARN_THROWS_WITH_AS_MESSAGE DOCTEST_WARN_THROWS_WITH_AS_MESSAGE
-#define WARN_NOTHROW_MESSAGE DOCTEST_WARN_NOTHROW_MESSAGE
-#define CHECK_MESSAGE DOCTEST_CHECK_MESSAGE
-#define CHECK_FALSE_MESSAGE DOCTEST_CHECK_FALSE_MESSAGE
-#define CHECK_THROWS_MESSAGE DOCTEST_CHECK_THROWS_MESSAGE
-#define CHECK_THROWS_AS_MESSAGE DOCTEST_CHECK_THROWS_AS_MESSAGE
-#define CHECK_THROWS_WITH_MESSAGE DOCTEST_CHECK_THROWS_WITH_MESSAGE
-#define CHECK_THROWS_WITH_AS_MESSAGE DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE
-#define CHECK_NOTHROW_MESSAGE DOCTEST_CHECK_NOTHROW_MESSAGE
-#define REQUIRE_MESSAGE DOCTEST_REQUIRE_MESSAGE
-#define REQUIRE_FALSE_MESSAGE DOCTEST_REQUIRE_FALSE_MESSAGE
-#define REQUIRE_THROWS_MESSAGE DOCTEST_REQUIRE_THROWS_MESSAGE
-#define REQUIRE_THROWS_AS_MESSAGE DOCTEST_REQUIRE_THROWS_AS_MESSAGE
-#define REQUIRE_THROWS_WITH_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_MESSAGE
-#define REQUIRE_THROWS_WITH_AS_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE
-#define REQUIRE_NOTHROW_MESSAGE DOCTEST_REQUIRE_NOTHROW_MESSAGE
-
-#define SCENARIO DOCTEST_SCENARIO
-#define SCENARIO_CLASS DOCTEST_SCENARIO_CLASS
-#define SCENARIO_TEMPLATE DOCTEST_SCENARIO_TEMPLATE
-#define SCENARIO_TEMPLATE_DEFINE DOCTEST_SCENARIO_TEMPLATE_DEFINE
-#define GIVEN DOCTEST_GIVEN
-#define WHEN DOCTEST_WHEN
-#define AND_WHEN DOCTEST_AND_WHEN
-#define THEN DOCTEST_THEN
-#define AND_THEN DOCTEST_AND_THEN
-
-#define WARN_EQ DOCTEST_WARN_EQ
-#define CHECK_EQ DOCTEST_CHECK_EQ
-#define REQUIRE_EQ DOCTEST_REQUIRE_EQ
-#define WARN_NE DOCTEST_WARN_NE
-#define CHECK_NE DOCTEST_CHECK_NE
-#define REQUIRE_NE DOCTEST_REQUIRE_NE
-#define WARN_GT DOCTEST_WARN_GT
-#define CHECK_GT DOCTEST_CHECK_GT
-#define REQUIRE_GT DOCTEST_REQUIRE_GT
-#define WARN_LT DOCTEST_WARN_LT
-#define CHECK_LT DOCTEST_CHECK_LT
-#define REQUIRE_LT DOCTEST_REQUIRE_LT
-#define WARN_GE DOCTEST_WARN_GE
-#define CHECK_GE DOCTEST_CHECK_GE
-#define REQUIRE_GE DOCTEST_REQUIRE_GE
-#define WARN_LE DOCTEST_WARN_LE
-#define CHECK_LE DOCTEST_CHECK_LE
-#define REQUIRE_LE DOCTEST_REQUIRE_LE
-#define WARN_UNARY DOCTEST_WARN_UNARY
-#define CHECK_UNARY DOCTEST_CHECK_UNARY
-#define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY
-#define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE
-#define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE
-#define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE
+#define REGISTER_EXCEPTION_TRANSLATOR(signature) DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature)
+#define REGISTER_REPORTER(name, priority, reporter) DOCTEST_REGISTER_REPORTER(name, priority, reporter)
+#define REGISTER_LISTENER(name, priority, reporter) DOCTEST_REGISTER_LISTENER(name, priority, reporter)
+#define INFO(...) DOCTEST_INFO(__VA_ARGS__)
+#define CAPTURE(x) DOCTEST_CAPTURE(x)
+#define ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_MESSAGE_AT(file, line, __VA_ARGS__)
+#define ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_FAIL_CHECK_AT(file, line, __VA_ARGS__)
+#define ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_FAIL_AT(file, line, __VA_ARGS__)
+#define MESSAGE(...) DOCTEST_MESSAGE(__VA_ARGS__)
+#define FAIL_CHECK(...) DOCTEST_FAIL_CHECK(__VA_ARGS__)
+#define FAIL(...) DOCTEST_FAIL(__VA_ARGS__)
+#define TO_LVALUE(...) DOCTEST_TO_LVALUE(__VA_ARGS__)
+
+#define WARN(...) DOCTEST_WARN(__VA_ARGS__)
+#define WARN_FALSE(...) DOCTEST_WARN_FALSE(__VA_ARGS__)
+#define WARN_THROWS(...) DOCTEST_WARN_THROWS(__VA_ARGS__)
+#define WARN_THROWS_AS(expr, ...) DOCTEST_WARN_THROWS_AS(expr, __VA_ARGS__)
+#define WARN_THROWS_WITH(expr, ...) DOCTEST_WARN_THROWS_WITH(expr, __VA_ARGS__)
+#define WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_WARN_THROWS_WITH_AS(expr, with, __VA_ARGS__)
+#define WARN_NOTHROW(...) DOCTEST_WARN_NOTHROW(__VA_ARGS__)
+#define CHECK(...) DOCTEST_CHECK(__VA_ARGS__)
+#define CHECK_FALSE(...) DOCTEST_CHECK_FALSE(__VA_ARGS__)
+#define CHECK_THROWS(...) DOCTEST_CHECK_THROWS(__VA_ARGS__)
+#define CHECK_THROWS_AS(expr, ...) DOCTEST_CHECK_THROWS_AS(expr, __VA_ARGS__)
+#define CHECK_THROWS_WITH(expr, ...) DOCTEST_CHECK_THROWS_WITH(expr, __VA_ARGS__)
+#define CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_AS(expr, with, __VA_ARGS__)
+#define CHECK_NOTHROW(...) DOCTEST_CHECK_NOTHROW(__VA_ARGS__)
+#define REQUIRE(...) DOCTEST_REQUIRE(__VA_ARGS__)
+#define REQUIRE_FALSE(...) DOCTEST_REQUIRE_FALSE(__VA_ARGS__)
+#define REQUIRE_THROWS(...) DOCTEST_REQUIRE_THROWS(__VA_ARGS__)
+#define REQUIRE_THROWS_AS(expr, ...) DOCTEST_REQUIRE_THROWS_AS(expr, __VA_ARGS__)
+#define REQUIRE_THROWS_WITH(expr, ...) DOCTEST_REQUIRE_THROWS_WITH(expr, __VA_ARGS__)
+#define REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, __VA_ARGS__)
+#define REQUIRE_NOTHROW(...) DOCTEST_REQUIRE_NOTHROW(__VA_ARGS__)
+
+#define WARN_MESSAGE(cond, ...) DOCTEST_WARN_MESSAGE(cond, __VA_ARGS__)
+#define WARN_FALSE_MESSAGE(cond, ...) DOCTEST_WARN_FALSE_MESSAGE(cond, __VA_ARGS__)
+#define WARN_THROWS_MESSAGE(expr, ...) DOCTEST_WARN_THROWS_MESSAGE(expr, __VA_ARGS__)
+#define WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__)
+#define WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__)
+#define WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__)
+#define WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_WARN_NOTHROW_MESSAGE(expr, __VA_ARGS__)
+#define CHECK_MESSAGE(cond, ...) DOCTEST_CHECK_MESSAGE(cond, __VA_ARGS__)
+#define CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_CHECK_FALSE_MESSAGE(cond, __VA_ARGS__)
+#define CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_CHECK_THROWS_MESSAGE(expr, __VA_ARGS__)
+#define CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__)
+#define CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__)
+#define CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__)
+#define CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_CHECK_NOTHROW_MESSAGE(expr, __VA_ARGS__)
+#define REQUIRE_MESSAGE(cond, ...) DOCTEST_REQUIRE_MESSAGE(cond, __VA_ARGS__)
+#define REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_REQUIRE_FALSE_MESSAGE(cond, __VA_ARGS__)
+#define REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_REQUIRE_THROWS_MESSAGE(expr, __VA_ARGS__)
+#define REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__)
+#define REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__)
+#define REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__)
+#define REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, __VA_ARGS__)
+
+#define SCENARIO(name) DOCTEST_SCENARIO(name)
+#define SCENARIO_CLASS(name) DOCTEST_SCENARIO_CLASS(name)
+#define SCENARIO_TEMPLATE(name, T, ...) DOCTEST_SCENARIO_TEMPLATE(name, T, __VA_ARGS__)
+#define SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id)
+#define GIVEN(name) DOCTEST_GIVEN(name)
+#define WHEN(name) DOCTEST_WHEN(name)
+#define AND_WHEN(name) DOCTEST_AND_WHEN(name)
+#define THEN(name) DOCTEST_THEN(name)
+#define AND_THEN(name) DOCTEST_AND_THEN(name)
+
+#define WARN_EQ(...) DOCTEST_WARN_EQ(__VA_ARGS__)
+#define CHECK_EQ(...) DOCTEST_CHECK_EQ(__VA_ARGS__)
+#define REQUIRE_EQ(...) DOCTEST_REQUIRE_EQ(__VA_ARGS__)
+#define WARN_NE(...) DOCTEST_WARN_NE(__VA_ARGS__)
+#define CHECK_NE(...) DOCTEST_CHECK_NE(__VA_ARGS__)
+#define REQUIRE_NE(...) DOCTEST_REQUIRE_NE(__VA_ARGS__)
+#define WARN_GT(...) DOCTEST_WARN_GT(__VA_ARGS__)
+#define CHECK_GT(...) DOCTEST_CHECK_GT(__VA_ARGS__)
+#define REQUIRE_GT(...) DOCTEST_REQUIRE_GT(__VA_ARGS__)
+#define WARN_LT(...) DOCTEST_WARN_LT(__VA_ARGS__)
+#define CHECK_LT(...) DOCTEST_CHECK_LT(__VA_ARGS__)
+#define REQUIRE_LT(...) DOCTEST_REQUIRE_LT(__VA_ARGS__)
+#define WARN_GE(...) DOCTEST_WARN_GE(__VA_ARGS__)
+#define CHECK_GE(...) DOCTEST_CHECK_GE(__VA_ARGS__)
+#define REQUIRE_GE(...) DOCTEST_REQUIRE_GE(__VA_ARGS__)
+#define WARN_LE(...) DOCTEST_WARN_LE(__VA_ARGS__)
+#define CHECK_LE(...) DOCTEST_CHECK_LE(__VA_ARGS__)
+#define REQUIRE_LE(...) DOCTEST_REQUIRE_LE(__VA_ARGS__)
+#define WARN_UNARY(...) DOCTEST_WARN_UNARY(__VA_ARGS__)
+#define CHECK_UNARY(...) DOCTEST_CHECK_UNARY(__VA_ARGS__)
+#define REQUIRE_UNARY(...) DOCTEST_REQUIRE_UNARY(__VA_ARGS__)
+#define WARN_UNARY_FALSE(...) DOCTEST_WARN_UNARY_FALSE(__VA_ARGS__)
+#define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__)
+#define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__)
// KEPT FOR BACKWARDS COMPATIBILITY
-#define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ
-#define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ
-#define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ
-#define FAST_WARN_NE DOCTEST_FAST_WARN_NE
-#define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE
-#define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE
-#define FAST_WARN_GT DOCTEST_FAST_WARN_GT
-#define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT
-#define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT
-#define FAST_WARN_LT DOCTEST_FAST_WARN_LT
-#define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT
-#define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT
-#define FAST_WARN_GE DOCTEST_FAST_WARN_GE
-#define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE
-#define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE
-#define FAST_WARN_LE DOCTEST_FAST_WARN_LE
-#define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE
-#define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE
-
-#define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY
-#define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY
-#define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY
-#define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE
-#define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE
-#define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE
-
-#define TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE
+#define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__)
+#define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__)
+#define FAST_REQUIRE_EQ(...) DOCTEST_FAST_REQUIRE_EQ(__VA_ARGS__)
+#define FAST_WARN_NE(...) DOCTEST_FAST_WARN_NE(__VA_ARGS__)
+#define FAST_CHECK_NE(...) DOCTEST_FAST_CHECK_NE(__VA_ARGS__)
+#define FAST_REQUIRE_NE(...) DOCTEST_FAST_REQUIRE_NE(__VA_ARGS__)
+#define FAST_WARN_GT(...) DOCTEST_FAST_WARN_GT(__VA_ARGS__)
+#define FAST_CHECK_GT(...) DOCTEST_FAST_CHECK_GT(__VA_ARGS__)
+#define FAST_REQUIRE_GT(...) DOCTEST_FAST_REQUIRE_GT(__VA_ARGS__)
+#define FAST_WARN_LT(...) DOCTEST_FAST_WARN_LT(__VA_ARGS__)
+#define FAST_CHECK_LT(...) DOCTEST_FAST_CHECK_LT(__VA_ARGS__)
+#define FAST_REQUIRE_LT(...) DOCTEST_FAST_REQUIRE_LT(__VA_ARGS__)
+#define FAST_WARN_GE(...) DOCTEST_FAST_WARN_GE(__VA_ARGS__)
+#define FAST_CHECK_GE(...) DOCTEST_FAST_CHECK_GE(__VA_ARGS__)
+#define FAST_REQUIRE_GE(...) DOCTEST_FAST_REQUIRE_GE(__VA_ARGS__)
+#define FAST_WARN_LE(...) DOCTEST_FAST_WARN_LE(__VA_ARGS__)
+#define FAST_CHECK_LE(...) DOCTEST_FAST_CHECK_LE(__VA_ARGS__)
+#define FAST_REQUIRE_LE(...) DOCTEST_FAST_REQUIRE_LE(__VA_ARGS__)
+
+#define FAST_WARN_UNARY(...) DOCTEST_FAST_WARN_UNARY(__VA_ARGS__)
+#define FAST_CHECK_UNARY(...) DOCTEST_FAST_CHECK_UNARY(__VA_ARGS__)
+#define FAST_REQUIRE_UNARY(...) DOCTEST_FAST_REQUIRE_UNARY(__VA_ARGS__)
+#define FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_WARN_UNARY_FALSE(__VA_ARGS__)
+#define FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_CHECK_UNARY_FALSE(__VA_ARGS__)
+#define FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_REQUIRE_UNARY_FALSE(__VA_ARGS__)
+
+#define TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, __VA_ARGS__)
#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
@@ -2644,6 +2746,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path")
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas")
@@ -2724,9 +2827,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <map>
#include <exception>
#include <stdexcept>
-#ifdef DOCTEST_CONFIG_POSIX_SIGNALS
#include <csignal>
-#endif // DOCTEST_CONFIG_POSIX_SIGNALS
#include <cfloat>
#include <cctype>
#include <cstdint>
@@ -2751,7 +2852,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#ifdef __AFXDLL
#include <AfxWin.h>
#else
-#include <Windows.h>
+#include <windows.h>
#endif
#include <io.h>
@@ -2762,6 +2863,12 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#endif // DOCTEST_PLATFORM_WINDOWS
+// this is a fix for https://github.com/onqtam/doctest/issues/348
+// https://mail.gnome.org/archives/xml/2012-January/msg00000.html
+#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO)
+#define STDOUT_FILENO fileno(stdout)
+#endif // HAVE_UNISTD_H
+
DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
// counts the number of elements in a C array
@@ -2781,12 +2888,24 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#define DOCTEST_THREAD_LOCAL thread_local
#endif
+#ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES
+#define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32
+#endif
+
+#ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE
+#define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64
+#endif
+
#ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
#define DOCTEST_OPTIONS_PREFIX_DISPLAY DOCTEST_CONFIG_OPTIONS_PREFIX
#else
#define DOCTEST_OPTIONS_PREFIX_DISPLAY ""
#endif
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+#define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+#endif
+
namespace doctest {
bool is_running_in_test = false;
@@ -2913,24 +3032,111 @@ typedef timer_large_integer::type ticks_t;
//unsigned int getElapsedMilliseconds() const {
// return static_cast<unsigned int>(getElapsedMicroseconds() / 1000);
//}
- double getElapsedSeconds() const { return (getCurrentTicks() - m_ticks) / 1000000.0; }
+ double getElapsedSeconds() const { return static_cast<double>(getCurrentTicks() - m_ticks) / 1000000.0; }
private:
ticks_t m_ticks = 0;
};
+#ifdef DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+ template <typename T>
+ using AtomicOrMultiLaneAtomic = std::atomic<T>;
+#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+ // Provides a multilane implementation of an atomic variable that supports add, sub, load,
+ // store. Instead of using a single atomic variable, this splits up into multiple ones,
+ // each sitting on a separate cache line. The goal is to provide a speedup when most
+ // operations are modifying. It achieves this with two properties:
+ //
+ // * Multiple atomics are used, so chance of congestion from the same atomic is reduced.
+ // * Each atomic sits on a separate cache line, so false sharing is reduced.
+ //
+ // The disadvantage is that there is a small overhead due to the use of TLS, and load/store
+ // is slower because all atomics have to be accessed.
+ template <typename T>
+ class MultiLaneAtomic
+ {
+ struct CacheLineAlignedAtomic
+ {
+ std::atomic<T> atomic{};
+ char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(std::atomic<T>)];
+ };
+ CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES];
+
+ static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE,
+ "guarantee one atomic takes exactly one cache line");
+
+ public:
+ T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; }
+
+ T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); }
+
+ T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
+ return myAtomic().fetch_add(arg, order);
+ }
+
+ T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
+ return myAtomic().fetch_sub(arg, order);
+ }
+
+ operator T() const DOCTEST_NOEXCEPT { return load(); }
+
+ T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT {
+ auto result = T();
+ for(auto const& c : m_atomics) {
+ result += c.atomic.load(order);
+ }
+ return result;
+ }
+
+ T operator=(T desired) DOCTEST_NOEXCEPT {
+ store(desired);
+ return desired;
+ }
+
+ void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT {
+ // first value becomes desired", all others become 0.
+ for(auto& c : m_atomics) {
+ c.atomic.store(desired, order);
+ desired = {};
+ }
+ }
+
+ private:
+ // Each thread has a different atomic that it operates on. If more than NumLanes threads
+ // use this, some will use the same atomic. So performance will degrate a bit, but still
+ // everything will work.
+ //
+ // The logic here is a bit tricky. The call should be as fast as possible, so that there
+ // is minimal to no overhead in determining the correct atomic for the current thread.
+ //
+ // 1. A global static counter laneCounter counts continuously up.
+ // 2. Each successive thread will use modulo operation of that counter so it gets an atomic
+ // assigned in a round-robin fashion.
+ // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with
+ // little overhead.
+ std::atomic<T>& myAtomic() DOCTEST_NOEXCEPT {
+ static std::atomic<size_t> laneCounter;
+ DOCTEST_THREAD_LOCAL size_t tlsLaneIdx =
+ laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES;
+
+ return m_atomics[tlsLaneIdx].atomic;
+ }
+ };
+
+ template <typename T>
+ using AtomicOrMultiLaneAtomic = MultiLaneAtomic<T>;
+#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS
+
// this holds both parameters from the command line and runtime data for tests
struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats
{
- std::atomic<int> numAssertsCurrentTest_atomic;
- std::atomic<int> numAssertsFailedCurrentTest_atomic;
+ AtomicOrMultiLaneAtomic<int> numAssertsCurrentTest_atomic;
+ AtomicOrMultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
std::vector<IReporter*> reporters_currently_used;
- const TestCase* currentTest = nullptr;
-
assert_handler ah = nullptr;
Timer timer;
@@ -3031,6 +3237,7 @@ String::String() {
String::~String() {
if(!isOnStack())
delete[] data.ptr;
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
}
String::String(const char* in)
@@ -3039,14 +3246,16 @@ String::String(const char* in)
String::String(const char* in, unsigned in_size) {
using namespace std;
if(in_size <= last) {
- memcpy(buf, in, in_size + 1);
+ memcpy(buf, in, in_size);
+ buf[in_size] = '\0';
setLast(last - in_size);
} else {
setOnHeap();
data.size = in_size;
data.capacity = data.size + 1;
data.ptr = new char[data.capacity];
- memcpy(data.ptr, in, in_size + 1);
+ memcpy(data.ptr, in, in_size);
+ data.ptr[in_size] = '\0';
}
}
@@ -3072,6 +3281,7 @@ String& String::operator+=(const String& other) {
if(total_size < len) {
// append to the current stack space
memcpy(buf + my_old_size, other.c_str(), other_size + 1);
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
setLast(last - total_size);
} else {
// alloc new chunk
@@ -3113,6 +3323,7 @@ String& String::operator+=(const String& other) {
return *this;
}
+// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
String String::operator+(const String& other) const { return String(*this) += other; }
String::String(String&& other) {
@@ -3267,6 +3478,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference")
DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference")
// depending on the current options this will remove the path of filenames
const char* skipPathFromFilename(const char* file) {
+#ifndef DOCTEST_CONFIG_DISABLE
if(getContextOptions()->no_path_in_filenames) {
auto back = std::strrchr(file, '\\');
auto forward = std::strrchr(file, '/');
@@ -3276,6 +3488,7 @@ const char* skipPathFromFilename(const char* file) {
return forward + 1;
}
}
+#endif // DOCTEST_CONFIG_DISABLE
return file;
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP
@@ -3294,6 +3507,7 @@ IContextScope::~IContextScope() = default;
#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
String toString(char* in) { return toString(static_cast<const char*>(in)); }
+// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; }
#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
String toString(bool in) { return in ? "true" : "false"; }
@@ -3366,6 +3580,7 @@ bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs
bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; }
String toString(const Approx& in) {
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
return String("Approx( ") + doctest::toString(in.m_value) + " )";
}
const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); }
@@ -3411,7 +3626,7 @@ int registerReporter(const char*, int, IReporter*) { return 0; }
namespace doctest_detail_test_suite_ns {
// holds the current test suite
doctest::detail::TestSuite& getCurrentTestSuite() {
- static doctest::detail::TestSuite data;
+ static doctest::detail::TestSuite data{};
return data;
}
} // namespace doctest_detail_test_suite_ns
@@ -3450,7 +3665,7 @@ namespace detail {
}
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
- [[noreturn]] void throwException() {
+ DOCTEST_NORETURN void throwException() {
g_cs->shouldLogCurrentException = false;
throw TestFailureException();
} // NOLINT(cert-err60-cpp)
@@ -3464,8 +3679,8 @@ namespace {
// matching of a string against a wildcard mask (case sensitivity configurable) taken from
// https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing
int wildcmp(const char* str, const char* wild, bool caseSensitive) {
- const char* cp = nullptr;
- const char* mp = nullptr;
+ const char* cp = str;
+ const char* mp = wild;
while((*str) && (*wild != '*')) {
if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) &&
@@ -3523,7 +3738,7 @@ namespace detail {
Subcase::Subcase(const String& name, const char* file, int line)
: m_signature({name, file, line}) {
- ContextState* s = g_cs;
+ auto* s = g_cs;
// check subcase filters
if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) {
@@ -3565,7 +3780,7 @@ namespace detail {
g_cs->subcasesPassed.insert(g_cs->subcasesStack);
g_cs->subcasesStack.pop_back();
-#if __cplusplus >= 201703L && defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411
+#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
if(std::uncaught_exceptions() > 0
#else
if(std::uncaught_exception()
@@ -3600,6 +3815,8 @@ namespace detail {
// clear state
m_description = nullptr;
m_skip = false;
+ m_no_breaks = false;
+ m_no_output = false;
m_may_fail = false;
m_should_fail = false;
m_expected_failures = 0;
@@ -3615,6 +3832,8 @@ namespace detail {
m_test_suite = test_suite.m_test_suite;
m_description = test_suite.m_description;
m_skip = test_suite.m_skip;
+ m_no_breaks = test_suite.m_no_breaks;
+ m_no_output = test_suite.m_no_output;
m_may_fail = test_suite.m_may_fail;
m_should_fail = test_suite.m_should_fail;
m_expected_failures = test_suite.m_expected_failures;
@@ -3658,25 +3877,31 @@ namespace detail {
}
bool TestCase::operator<(const TestCase& other) const {
+ // this will be used only to differentiate between test cases - not relevant for sorting
if(m_line != other.m_line)
return m_line < other.m_line;
- const int file_cmp = std::strcmp(m_file, other.m_file);
+ const int name_cmp = strcmp(m_name, other.m_name);
+ if(name_cmp != 0)
+ return name_cmp < 0;
+ const int file_cmp = m_file.compare(other.m_file);
if(file_cmp != 0)
return file_cmp < 0;
return m_template_id < other.m_template_id;
}
+
+ // all the registered tests
+ std::set<TestCase>& getRegisteredTests() {
+ static std::set<TestCase> data;
+ return data;
+ }
} // namespace detail
namespace {
using namespace detail;
// for sorting tests by file/line
bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) {
-#if DOCTEST_MSVC
// this is needed because MSVC gives different case for drive letters
// for __FILE__ when evaluated in a header and a source file
- const int res = doctest::stricmp(lhs->m_file, rhs->m_file);
-#else // MSVC
- const int res = std::strcmp(lhs->m_file, rhs->m_file);
-#endif // MSVC
+ const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC));
if(res != 0)
return res < 0;
if(lhs->m_line != rhs->m_line)
@@ -3700,12 +3925,6 @@ namespace {
return suiteOrderComparator(lhs, rhs);
}
- // all the registered tests
- std::set<TestCase>& getRegisteredTests() {
- static std::set<TestCase> data;
- return data;
- }
-
#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
HANDLE g_stdoutHandle;
WORD g_origFgAttrs;
@@ -3731,8 +3950,8 @@ namespace {
DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
void color_to_stream(std::ostream& s, Color::Enum code) {
- ((void)s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS
- ((void)code); // for DOCTEST_CONFIG_COLORS_NONE
+ static_cast<void>(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS
+ static_cast<void>(code); // for DOCTEST_CONFIG_COLORS_NONE
#ifdef DOCTEST_CONFIG_COLORS_ANSI
if(g_no_colors ||
(isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false))
@@ -3838,7 +4057,28 @@ namespace detail {
#ifdef DOCTEST_IS_DEBUGGER_ACTIVE
bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); }
#else // DOCTEST_IS_DEBUGGER_ACTIVE
-#ifdef DOCTEST_PLATFORM_MAC
+#ifdef DOCTEST_PLATFORM_LINUX
+ class ErrnoGuard {
+ public:
+ ErrnoGuard() : m_oldErrno(errno) {}
+ ~ErrnoGuard() { errno = m_oldErrno; }
+ private:
+ int m_oldErrno;
+ };
+ // See the comments in Catch2 for the reasoning behind this implementation:
+ // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102
+ bool isDebuggerActive() {
+ ErrnoGuard guard;
+ std::ifstream in("/proc/self/status");
+ for(std::string line; std::getline(in, line);) {
+ static const int PREFIX_LEN = 11;
+ if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) {
+ return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+ }
+ }
+ return false;
+ }
+#elif defined(DOCTEST_PLATFORM_MAC)
// The following function is taken directly from the following technical note:
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
// Returns true if the current process is being debugged (either
@@ -3865,7 +4105,7 @@ namespace detail {
// We're being debugged if the P_TRACED flag is set.
return ((info.kp_proc.p_flag & P_TRACED) != 0);
}
-#elif DOCTEST_MSVC || defined(__MINGW32__)
+#elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__)
bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; }
#else
bool isDebuggerActive() { return false; }
@@ -3913,7 +4153,7 @@ namespace detail {
// ContextScope has been destroyed (base class destructors run after derived class destructors).
// Instead, ContextScope calls this method directly from its destructor.
void ContextScopeBase::destroy() {
-#if __cplusplus >= 201703L && defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411
+#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
if(std::uncaught_exceptions() > 0) {
#else
if(std::uncaught_exception()) {
@@ -3932,19 +4172,12 @@ namespace detail {
namespace {
using namespace detail;
- std::ostream& file_line_to_stream(std::ostream& s, const char* file, int line,
- const char* tail = "") {
- const auto opt = getContextOptions();
- s << Color::LightGrey << skipPathFromFilename(file) << (opt->gnu_file_line ? ":" : "(")
- << (opt->no_line_numbers ? 0 : line) // 0 or the real num depending on the option
- << (opt->gnu_file_line ? ":" : "):") << tail;
- return s;
- }
-
#if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
struct FatalConditionHandler
{
- void reset() {}
+ static void reset() {}
+ static void allocateAltStackMem() {}
+ static void freeAltStackMem() {}
};
#else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
@@ -3961,26 +4194,45 @@ namespace {
// Windows can easily distinguish between SO and SigSegV,
// but SigInt, SigTerm, etc are handled differently.
SignalDefs signalDefs[] = {
- {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"},
- {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"},
- {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"},
- {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"},
+ {static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION),
+ "SIGILL - Illegal instruction signal"},
+ {static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"},
+ {static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION),
+ "SIGSEGV - Segmentation violation signal"},
+ {static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"},
};
struct FatalConditionHandler
{
static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) {
- for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
- if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
- reportFatal(signalDefs[i].name);
- break;
+ // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the
+ // console just once no matter how many threads have crashed.
+ static std::mutex mutex;
+ static bool execute = true;
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ if(execute) {
+ bool reported = false;
+ for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
+ if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
+ reportFatal(signalDefs[i].name);
+ reported = true;
+ break;
+ }
+ }
+ if(reported == false)
+ reportFatal("Unhandled SEH exception caught");
+ if(isDebuggerActive() && !g_cs->no_breaks)
+ DOCTEST_BREAK_INTO_DEBUGGER();
}
+ execute = false;
}
- // If its not an exception we care about, pass it along.
- // This stops us from eating debugger breaks etc.
- return EXCEPTION_CONTINUE_SEARCH;
+ std::exit(EXIT_FAILURE);
}
+ static void allocateAltStackMem() {}
+ static void freeAltStackMem() {}
+
FatalConditionHandler() {
isSet = true;
// 32k seems enough for doctest to handle stack overflow,
@@ -3990,6 +4242,51 @@ namespace {
previousTop = SetUnhandledExceptionFilter(handleException);
// Pass in guarantee size to be filled
SetThreadStackGuarantee(&guaranteeSize);
+
+ // On Windows uncaught exceptions from another thread, exceptions from
+ // destructors, or calls to std::terminate are not a SEH exception
+
+ // The terminal handler gets called when:
+ // - std::terminate is called FROM THE TEST RUNNER THREAD
+ // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD
+ original_terminate_handler = std::get_terminate();
+ std::set_terminate([]() DOCTEST_NOEXCEPT {
+ reportFatal("Terminate handler called");
+ if(isDebuggerActive() && !g_cs->no_breaks)
+ DOCTEST_BREAK_INTO_DEBUGGER();
+ std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well
+ });
+
+ // SIGABRT is raised when:
+ // - std::terminate is called FROM A DIFFERENT THREAD
+ // - an exception is thrown from a destructor FROM A DIFFERENT THREAD
+ // - an uncaught exception is thrown FROM A DIFFERENT THREAD
+ prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT {
+ if(signal == SIGABRT) {
+ reportFatal("SIGABRT - Abort (abnormal termination) signal");
+ if(isDebuggerActive() && !g_cs->no_breaks)
+ DOCTEST_BREAK_INTO_DEBUGGER();
+ std::exit(EXIT_FAILURE);
+ }
+ });
+
+ // The following settings are taken from google test, and more
+ // specifically from UnitTest::Run() inside of gtest.cc
+
+ // the user does not want to see pop-up dialogs about crashes
+ prev_error_mode_1 = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
+ SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
+ // This forces the abort message to go to stderr in all circumstances.
+ prev_error_mode_2 = _set_error_mode(_OUT_TO_STDERR);
+ // In the debug version, Visual Studio pops up a separate dialog
+ // offering a choice to debug the aborted program - we want to disable that.
+ prev_abort_behavior = _set_abort_behavior(0x0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
+ // In debug mode, the Windows CRT can crash with an assertion over invalid
+ // input (e.g. passing an invalid file descriptor). The default handling
+ // for these assertions is to pop up a dialog and wait for user input.
+ // Instead ask the CRT to dump such assertions to stderr non-interactively.
+ prev_report_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ prev_report_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
}
static void reset() {
@@ -3997,7 +4294,13 @@ namespace {
// Unregister handler and restore the old guarantee
SetUnhandledExceptionFilter(previousTop);
SetThreadStackGuarantee(&guaranteeSize);
- previousTop = nullptr;
+ std::set_terminate(original_terminate_handler);
+ std::signal(SIGABRT, prev_sigabrt_handler);
+ SetErrorMode(prev_error_mode_1);
+ _set_error_mode(prev_error_mode_2);
+ _set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
+ static_cast<void>(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode));
+ static_cast<void>(_CrtSetReportFile(_CRT_ASSERT, prev_report_file));
isSet = false;
}
}
@@ -4005,11 +4308,25 @@ namespace {
~FatalConditionHandler() { reset(); }
private:
+ static UINT prev_error_mode_1;
+ static int prev_error_mode_2;
+ static unsigned int prev_abort_behavior;
+ static int prev_report_mode;
+ static _HFILE prev_report_file;
+ static void (*prev_sigabrt_handler)(int);
+ static std::terminate_handler original_terminate_handler;
static bool isSet;
static ULONG guaranteeSize;
static LPTOP_LEVEL_EXCEPTION_FILTER previousTop;
};
+ UINT FatalConditionHandler::prev_error_mode_1;
+ int FatalConditionHandler::prev_error_mode_2;
+ unsigned int FatalConditionHandler::prev_abort_behavior;
+ int FatalConditionHandler::prev_report_mode;
+ _HFILE FatalConditionHandler::prev_report_file;
+ void (*FatalConditionHandler::prev_sigabrt_handler)(int);
+ std::terminate_handler FatalConditionHandler::original_terminate_handler;
bool FatalConditionHandler::isSet = false;
ULONG FatalConditionHandler::guaranteeSize = 0;
LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr;
@@ -4033,7 +4350,8 @@ namespace {
static bool isSet;
static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)];
static stack_t oldSigStack;
- static char altStackMem[4 * SIGSTKSZ];
+ static size_t altStackSize;
+ static char* altStackMem;
static void handleSignal(int sig) {
const char* name = "<unknown signal>";
@@ -4049,11 +4367,19 @@ namespace {
raise(sig);
}
+ static void allocateAltStackMem() {
+ altStackMem = new char[altStackSize];
+ }
+
+ static void freeAltStackMem() {
+ delete[] altStackMem;
+ }
+
FatalConditionHandler() {
isSet = true;
stack_t sigStack;
sigStack.ss_sp = altStackMem;
- sigStack.ss_size = sizeof(altStackMem);
+ sigStack.ss_size = altStackSize;
sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = {};
@@ -4078,10 +4404,11 @@ namespace {
}
};
- bool FatalConditionHandler::isSet = false;
+ bool FatalConditionHandler::isSet = false;
struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {};
- stack_t FatalConditionHandler::oldSigStack = {};
- char FatalConditionHandler::altStackMem[] = {};
+ stack_t FatalConditionHandler::oldSigStack = {};
+ size_t FatalConditionHandler::altStackSize = 4 * SIGSTKSZ;
+ char* FatalConditionHandler::altStackMem = nullptr;
#endif // DOCTEST_PLATFORM_WINDOWS
#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
@@ -4183,8 +4510,8 @@ namespace detail {
failed_out_of_a_testing_context(*this);
}
- return m_failed && isDebuggerActive() &&
- !getContextOptions()->no_breaks; // break into debugger
+ return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks &&
+ (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger
}
void ResultBuilder::react() const {
@@ -4209,6 +4536,7 @@ namespace detail {
// ###################################################################################
DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp);
DOCTEST_ASSERT_IN_TESTS(result.m_decomp);
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
}
MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) {
@@ -4233,7 +4561,8 @@ namespace detail {
addFailedAssert(m_severity);
}
- return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn; // break
+ return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn &&
+ (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger
}
void MessageBuilder::react() {
@@ -4247,7 +4576,7 @@ namespace {
using namespace detail;
template <typename Ex>
- [[noreturn]] void throw_exception(Ex const& e) {
+ DOCTEST_NORETURN void throw_exception(Ex const& e) {
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
throw e;
#else // DOCTEST_CONFIG_NO_EXCEPTIONS
@@ -4257,9 +4586,11 @@ namespace {
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
}
+#ifndef DOCTEST_INTERNAL_ERROR
#define DOCTEST_INTERNAL_ERROR(msg) \
throw_exception(std::logic_error( \
__FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg))
+#endif // DOCTEST_INTERNAL_ERROR
// clang-format off
@@ -4290,8 +4621,8 @@ namespace {
public:
ScopedElement( XmlWriter* writer );
- ScopedElement( ScopedElement&& other ) noexcept;
- ScopedElement& operator=( ScopedElement&& other ) noexcept;
+ ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT;
+ ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT;
~ScopedElement();
@@ -4508,11 +4839,11 @@ namespace {
: m_writer( writer )
{}
- XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
+ XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT
: m_writer( other.m_writer ){
other.m_writer = nullptr;
}
- XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
+ XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT {
if ( m_writer ) {
m_writer->endElement();
}
@@ -4691,7 +5022,7 @@ namespace {
tc = &in;
xml.startElement("TestCase")
.writeAttribute("name", in.m_name)
- .writeAttribute("filename", skipPathFromFilename(in.m_file))
+ .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str()))
.writeAttribute("line", line(in.m_line))
.writeAttribute("description", in.m_description);
@@ -4722,7 +5053,7 @@ namespace {
for(unsigned i = 0; i < in.num_data; ++i) {
xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name)
.writeAttribute("testsuite", in.data[i]->m_test_suite)
- .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file))
+ .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str()))
.writeAttribute("line", line(in.data[i]->m_line));
}
xml.scopedElement("OverallResultsTestCases")
@@ -4878,6 +5209,279 @@ namespace {
DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter);
+ void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) {
+ if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) ==
+ 0) //!OCLINT bitwise operator in conditional
+ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) "
+ << Color::None;
+
+ if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
+ s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n";
+ } else if((rb.m_at & assertType::is_throws_as) &&
+ (rb.m_at & assertType::is_throws_with)) { //!OCLINT
+ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
+ << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None;
+ if(rb.m_threw) {
+ if(!rb.m_failed) {
+ s << "threw as expected!\n";
+ } else {
+ s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n";
+ }
+ } else {
+ s << "did NOT throw at all!\n";
+ }
+ } else if(rb.m_at &
+ assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
+ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", "
+ << rb.m_exception_type << " ) " << Color::None
+ << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" :
+ "threw a DIFFERENT exception: ") :
+ "did NOT throw at all!")
+ << Color::Cyan << rb.m_exception << "\n";
+ } else if(rb.m_at &
+ assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
+ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
+ << rb.m_exception_string << "\" ) " << Color::None
+ << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" :
+ "threw a DIFFERENT exception: ") :
+ "did NOT throw at all!")
+ << Color::Cyan << rb.m_exception << "\n";
+ } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
+ s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan
+ << rb.m_exception << "\n";
+ } else {
+ s << (rb.m_threw ? "THREW exception: " :
+ (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n"));
+ if(rb.m_threw)
+ s << rb.m_exception << "\n";
+ else
+ s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n";
+ }
+ }
+
+ // TODO:
+ // - log_message()
+ // - respond to queries
+ // - honor remaining options
+ // - more attributes in tags
+ struct JUnitReporter : public IReporter
+ {
+ XmlWriter xml;
+ std::mutex mutex;
+ Timer timer;
+ std::vector<String> deepestSubcaseStackNames;
+
+ struct JUnitTestCaseData
+ {
+ static std::string getCurrentTimestamp() {
+ // Beware, this is not reentrant because of backward compatibility issues
+ // Also, UTC only, again because of backward compatibility (%z is C++11)
+ time_t rawtime;
+ std::time(&rawtime);
+ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+ std::tm timeInfo;
+#ifdef DOCTEST_PLATFORM_WINDOWS
+ gmtime_s(&timeInfo, &rawtime);
+#else // DOCTEST_PLATFORM_WINDOWS
+ gmtime_r(&rawtime, &timeInfo);
+#endif // DOCTEST_PLATFORM_WINDOWS
+
+ char timeStamp[timeStampSize];
+ const char* const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+ return std::string(timeStamp);
+ }
+
+ struct JUnitTestMessage
+ {
+ JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details)
+ : message(_message), type(_type), details(_details) {}
+
+ JUnitTestMessage(const std::string& _message, const std::string& _details)
+ : message(_message), type(), details(_details) {}
+
+ std::string message, type, details;
+ };
+
+ struct JUnitTestCase
+ {
+ JUnitTestCase(const std::string& _classname, const std::string& _name)
+ : classname(_classname), name(_name), time(0), failures() {}
+
+ std::string classname, name;
+ double time;
+ std::vector<JUnitTestMessage> failures, errors;
+ };
+
+ void add(const std::string& classname, const std::string& name) {
+ testcases.emplace_back(classname, name);
+ }
+
+ void appendSubcaseNamesToLastTestcase(std::vector<String> nameStack) {
+ for(auto& curr: nameStack)
+ if(curr.size())
+ testcases.back().name += std::string("/") + curr.c_str();
+ }
+
+ void addTime(double time) {
+ if(time < 1e-4)
+ time = 0;
+ testcases.back().time = time;
+ totalSeconds += time;
+ }
+
+ void addFailure(const std::string& message, const std::string& type, const std::string& details) {
+ testcases.back().failures.emplace_back(message, type, details);
+ ++totalFailures;
+ }
+
+ void addError(const std::string& message, const std::string& details) {
+ testcases.back().errors.emplace_back(message, details);
+ ++totalErrors;
+ }
+
+ std::vector<JUnitTestCase> testcases;
+ double totalSeconds = 0;
+ int totalErrors = 0, totalFailures = 0;
+ };
+
+ JUnitTestCaseData testCaseData;
+
+ // caching pointers/references to objects of these types - safe to do
+ const ContextOptions& opt;
+ const TestCaseData* tc = nullptr;
+
+ JUnitReporter(const ContextOptions& co)
+ : xml(*co.cout)
+ , opt(co) {}
+
+ unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; }
+
+ // =========================================================================================
+ // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
+ // =========================================================================================
+
+ void report_query(const QueryData&) override {}
+
+ void test_run_start() override {}
+
+ void test_run_end(const TestRunStats& p) override {
+ // remove .exe extension - mainly to have the same output on UNIX and Windows
+ std::string binary_name = skipPathFromFilename(opt.binary_name.c_str());
+#ifdef DOCTEST_PLATFORM_WINDOWS
+ if(binary_name.rfind(".exe") != std::string::npos)
+ binary_name = binary_name.substr(0, binary_name.length() - 4);
+#endif // DOCTEST_PLATFORM_WINDOWS
+ xml.startElement("testsuites");
+ xml.startElement("testsuite").writeAttribute("name", binary_name)
+ .writeAttribute("errors", testCaseData.totalErrors)
+ .writeAttribute("failures", testCaseData.totalFailures)
+ .writeAttribute("tests", p.numAsserts);
+ if(opt.no_time_in_output == false) {
+ xml.writeAttribute("time", testCaseData.totalSeconds);
+ xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp());
+ }
+ if(opt.no_version == false)
+ xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR);
+
+ for(const auto& testCase : testCaseData.testcases) {
+ xml.startElement("testcase")
+ .writeAttribute("classname", testCase.classname)
+ .writeAttribute("name", testCase.name);
+ if(opt.no_time_in_output == false)
+ xml.writeAttribute("time", testCase.time);
+ // This is not ideal, but it should be enough to mimic gtest's junit output.
+ xml.writeAttribute("status", "run");
+
+ for(const auto& failure : testCase.failures) {
+ xml.scopedElement("failure")
+ .writeAttribute("message", failure.message)
+ .writeAttribute("type", failure.type)
+ .writeText(failure.details, false);
+ }
+
+ for(const auto& error : testCase.errors) {
+ xml.scopedElement("error")
+ .writeAttribute("message", error.message)
+ .writeText(error.details);
+ }
+
+ xml.endElement();
+ }
+ xml.endElement();
+ xml.endElement();
+ }
+
+ void test_case_start(const TestCaseData& in) override {
+ testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name);
+ timer.start();
+ }
+
+ void test_case_reenter(const TestCaseData& in) override {
+ testCaseData.addTime(timer.getElapsedSeconds());
+ testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames);
+ deepestSubcaseStackNames.clear();
+
+ timer.start();
+ testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name);
+ }
+
+ void test_case_end(const CurrentTestCaseStats&) override {
+ testCaseData.addTime(timer.getElapsedSeconds());
+ testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames);
+ deepestSubcaseStackNames.clear();
+ }
+
+ void test_case_exception(const TestCaseException& e) override {
+ std::lock_guard<std::mutex> lock(mutex);
+ testCaseData.addError("exception", e.error_string.c_str());
+ }
+
+ void subcase_start(const SubcaseSignature& in) override {
+ std::lock_guard<std::mutex> lock(mutex);
+ deepestSubcaseStackNames.push_back(in.m_name);
+ }
+
+ void subcase_end() override {}
+
+ void log_assert(const AssertData& rb) override {
+ if(!rb.m_failed) // report only failures & ignore the `success` option
+ return;
+
+ std::lock_guard<std::mutex> lock(mutex);
+
+ std::ostringstream os;
+ os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(")
+ << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl;
+
+ fulltext_log_assert_to_stream(os, rb);
+ log_contexts(os);
+ testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str());
+ }
+
+ void log_message(const MessageData&) override {}
+
+ void test_case_skipped(const TestCaseData&) override {}
+
+ void log_contexts(std::ostringstream& s) {
+ int num_contexts = get_num_active_contexts();
+ if(num_contexts) {
+ auto contexts = get_active_contexts();
+
+ s << " logged: ";
+ for(int i = 0; i < num_contexts; ++i) {
+ s << (i == 0 ? "" : " ");
+ contexts[i]->stringify(&s);
+ s << std::endl;
+ }
+ }
+ }
+ };
+
+ DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter);
+
struct Whitespace
{
int nrSpaces;
@@ -4896,6 +5500,7 @@ namespace {
std::ostream& s;
bool hasLoggedCurrentTestStart;
std::vector<SubcaseSignature> subcasesStack;
+ size_t currentSubcaseLevel;
std::mutex mutex;
// caching pointers/references to objects of these types - safe to do
@@ -4954,23 +5559,40 @@ namespace {
s << "\n";
}
+ // this was requested to be made virtual so users could override it
+ virtual void file_line_to_stream(const char* file, int line,
+ const char* tail = "") {
+ s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(")
+ << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option
+ << (opt.gnu_file_line ? ":" : "):") << tail;
+ }
+
void logTestStart() {
if(hasLoggedCurrentTestStart)
return;
separator_to_stream();
- file_line_to_stream(s, tc->m_file, tc->m_line, "\n");
+ file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n");
if(tc->m_description)
s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n";
if(tc->m_test_suite && tc->m_test_suite[0] != '\0')
s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n";
if(strncmp(tc->m_name, " Scenario:", 11) != 0)
- s << Color::None << "TEST CASE: ";
+ s << Color::Yellow << "TEST CASE: ";
s << Color::None << tc->m_name << "\n";
- for(auto& curr : subcasesStack)
- if(curr.m_name[0] != '\0')
- s << " " << curr.m_name << "\n";
+ for(size_t i = 0; i < currentSubcaseLevel; ++i) {
+ if(subcasesStack[i].m_name[0] != '\0')
+ s << " " << subcasesStack[i].m_name << "\n";
+ }
+
+ if(currentSubcaseLevel != subcasesStack.size()) {
+ s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None;
+ for(size_t i = 0; i < subcasesStack.size(); ++i) {
+ if(subcasesStack[i].m_name[0] != '\0')
+ s << " " << subcasesStack[i].m_name << "\n";
+ }
+ }
s << "\n";
@@ -5048,7 +5670,7 @@ namespace {
<< Whitespace(sizePrefixDisplay*1) << "output filename\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by=<string> "
<< Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n";
- s << Whitespace(sizePrefixDisplay*3) << " <string> - by [file/suite/name/rand]\n";
+ s << Whitespace(sizePrefixDisplay*3) << " <string> - [file/suite/name/rand/none]\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed=<int> "
<< Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n";
s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first=<int> "
@@ -5181,25 +5803,28 @@ namespace {
separator_to_stream();
s << std::dec;
+ auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
+ auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
+ auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0;
- s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(6)
+ s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth)
<< p.numTestCasesPassingFilters << " | "
<< ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None :
Color::Green)
- << std::setw(6) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed"
+ << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed"
<< Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None)
- << std::setw(6) << p.numTestCasesFailed << " failed" << Color::None << " | ";
+ << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |";
if(opt.no_skipped_summary == false) {
const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters;
- s << (numSkipped == 0 ? Color::None : Color::Yellow) << std::setw(6) << numSkipped
+ s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped
<< " skipped" << Color::None;
}
s << "\n";
- s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(6)
+ s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth)
<< p.numAsserts << " | "
<< ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green)
- << std::setw(6) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None
- << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(6)
+ << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None
+ << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth)
<< p.numAssertsFailed << " failed" << Color::None << " |\n";
s << Color::Cyan << "[doctest] " << Color::None
<< "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green)
@@ -5209,11 +5834,18 @@ namespace {
void test_case_start(const TestCaseData& in) override {
hasLoggedCurrentTestStart = false;
tc = &in;
+ subcasesStack.clear();
+ currentSubcaseLevel = 0;
}
- void test_case_reenter(const TestCaseData&) override {}
+ void test_case_reenter(const TestCaseData&) override {
+ subcasesStack.clear();
+ }
void test_case_end(const CurrentTestCaseStats& st) override {
+ if(tc->m_no_output)
+ return;
+
// log the preamble of the test case only if there is something
// else to print - something other than that an assert has failed
if(opt.duration ||
@@ -5248,9 +5880,12 @@ namespace {
}
void test_case_exception(const TestCaseException& e) override {
+ if(tc->m_no_output)
+ return;
+
logTestStart();
- file_line_to_stream(s, tc->m_file, tc->m_line, " ");
+ file_line_to_stream(tc->m_file.c_str(), tc->m_line, " ");
successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require :
assertType::is_check);
s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ")
@@ -5271,82 +5906,41 @@ namespace {
void subcase_start(const SubcaseSignature& subc) override {
std::lock_guard<std::mutex> lock(mutex);
subcasesStack.push_back(subc);
+ ++currentSubcaseLevel;
hasLoggedCurrentTestStart = false;
}
void subcase_end() override {
std::lock_guard<std::mutex> lock(mutex);
- subcasesStack.pop_back();
+ --currentSubcaseLevel;
hasLoggedCurrentTestStart = false;
}
void log_assert(const AssertData& rb) override {
- if(!rb.m_failed && !opt.success)
+ if((!rb.m_failed && !opt.success) || tc->m_no_output)
return;
std::lock_guard<std::mutex> lock(mutex);
logTestStart();
- file_line_to_stream(s, rb.m_file, rb.m_line, " ");
+ file_line_to_stream(rb.m_file, rb.m_line, " ");
successOrFailColoredStringToStream(!rb.m_failed, rb.m_at);
- if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) ==
- 0) //!OCLINT bitwise operator in conditional
- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) "
- << Color::None;
-
- if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
- s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n";
- } else if((rb.m_at & assertType::is_throws_as) &&
- (rb.m_at & assertType::is_throws_with)) { //!OCLINT
- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
- << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None;
- if(rb.m_threw) {
- if(!rb.m_failed) {
- s << "threw as expected!\n";
- } else {
- s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n";
- }
- } else {
- s << "did NOT throw at all!\n";
- }
- } else if(rb.m_at &
- assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", "
- << rb.m_exception_type << " ) " << Color::None
- << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" :
- "threw a DIFFERENT exception: ") :
- "did NOT throw at all!")
- << Color::Cyan << rb.m_exception << "\n";
- } else if(rb.m_at &
- assertType::is_throws_with) { //!OCLINT bitwise operator in conditional
- s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \""
- << rb.m_exception_string << "\" ) " << Color::None
- << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" :
- "threw a DIFFERENT exception: ") :
- "did NOT throw at all!")
- << Color::Cyan << rb.m_exception << "\n";
- } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
- s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan
- << rb.m_exception << "\n";
- } else {
- s << (rb.m_threw ? "THREW exception: " :
- (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n"));
- if(rb.m_threw)
- s << rb.m_exception << "\n";
- else
- s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n";
- }
+
+ fulltext_log_assert_to_stream(s, rb);
log_contexts();
}
void log_message(const MessageData& mb) override {
+ if(tc->m_no_output)
+ return;
+
std::lock_guard<std::mutex> lock(mutex);
logTestStart();
- file_line_to_stream(s, mb.m_file, mb.m_line, " ");
+ file_line_to_stream(mb.m_file, mb.m_line, " ");
s << getSuccessOrFailColor(false, mb.m_severity)
<< getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity,
"MESSAGE") << ": ";
@@ -5372,8 +5966,10 @@ namespace {
bool with_col = g_no_colors; \
g_no_colors = false; \
ConsoleReporter::func(arg); \
- DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \
- oss.str(""); \
+ if(oss.tellp() != std::streampos{}) { \
+ DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \
+ oss.str(""); \
+ } \
g_no_colors = with_col; \
}
@@ -5560,7 +6156,7 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) {
#define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \
if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \
parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \
- p->var = !!intRes; \
+ p->var = static_cast<bool>(intRes); \
else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \
parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \
p->var = true; \
@@ -5606,7 +6202,9 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) {
DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC));
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false);
DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false);
// clang-format on
if(withDefaults) {
@@ -5662,6 +6260,7 @@ void Context::clearFilters() {
// allows the user to override procedurally the int/bool options from the command line
void Context::setOption(const char* option, int value) {
setOption(option, toString(value).c_str());
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
}
// allows the user to override procedurally the string options from the command line
@@ -5702,7 +6301,11 @@ int Context::run() {
p->cout = &fstr;
}
+ FatalConditionHandler::allocateAltStackMem();
+
auto cleanup_and_return = [&]() {
+ FatalConditionHandler::freeAltStackMem();
+
if(fstr.is_open())
fstr.close();
@@ -5737,7 +6340,7 @@ int Context::run() {
p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs));
#ifdef DOCTEST_PLATFORM_WINDOWS
- if(isDebuggerActive())
+ if(isDebuggerActive() && p->no_debug_output == false)
p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs));
#endif // DOCTEST_PLATFORM_WINDOWS
@@ -5774,6 +6377,9 @@ int Context::run() {
first[i] = first[idxToSwap];
first[idxToSwap] = temp;
}
+ } else if(p->order_by.compare("none", true) == 0) {
+ // means no sorting - beneficial for death tests which call into the executable
+ // with a specific test case in mind - we don't want to slow down the startup times
}
}
@@ -5793,9 +6399,9 @@ int Context::run() {
if(tc.m_skip && !p->no_skip)
skip_me = true;
- if(!matchesAny(tc.m_file, p->filters[0], true, p->case_sensitive))
+ if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive))
skip_me = true;
- if(matchesAny(tc.m_file, p->filters[1], false, p->case_sensitive))
+ if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive))
skip_me = true;
if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive))
skip_me = true;
@@ -5873,10 +6479,13 @@ int Context::run() {
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
try {
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+// MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method)
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable
FatalConditionHandler fatalConditionHandler; // Handle signals
// execute the test
tc.m_test();
fatalConditionHandler.reset();
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
} catch(const TestFailureException&) {
p->failure_flags |= TestCaseFailureReason::AssertFailure;
@@ -5968,4 +6577,4 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
DOCTEST_GCC_SUPPRESS_WARNING_POP
#endif // DOCTEST_LIBRARY_IMPLEMENTATION
-#endif // DOCTEST_CONFIG_IMPLEMENT
+#endif // DOCTEST_CONFIG_IMPLEMENT \ No newline at end of file
diff --git a/third_party/include/nlohmann/LICENSE.MIT b/third_party/include/nlohmann/LICENSE.MIT
index 00599af..f0622d6 100644
--- a/third_party/include/nlohmann/LICENSE.MIT
+++ b/third_party/include/nlohmann/LICENSE.MIT
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2013-2017 Niels Lohmann
+Copyright (c) 2013-2021 Niels Lohmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/third_party/include/nlohmann/json.hpp b/third_party/include/nlohmann/json.hpp
index cc822a5..34a8d32 100644
--- a/third_party/include/nlohmann/json.hpp
+++ b/third_party/include/nlohmann/json.hpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 3.8.0
+| | |__ | | | | | | version 3.9.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -31,11 +31,10 @@ SOFTWARE.
#define INCLUDE_NLOHMANN_JSON_HPP_
#define NLOHMANN_JSON_VERSION_MAJOR 3
-#define NLOHMANN_JSON_VERSION_MINOR 8
-#define NLOHMANN_JSON_VERSION_PATCH 0
+#define NLOHMANN_JSON_VERSION_MINOR 9
+#define NLOHMANN_JSON_VERSION_PATCH 1
#include <algorithm> // all_of, find, for_each
-#include <cassert> // assert
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
#include <functional> // hash, less
#include <initializer_list> // initializer_list
@@ -50,6 +49,7 @@ SOFTWARE.
// #include <nlohmann/adl_serializer.hpp>
+#include <type_traits>
#include <utility>
// #include <nlohmann/detail/conversions/from_json.hpp>
@@ -67,57 +67,108 @@ SOFTWARE.
#include <utility> // pair, declval
#include <valarray> // valarray
-// #include <nlohmann/detail/boolean_operators.hpp>
-
-
-// Header <ciso646> is removed in C++20.
-// See <https://github.com/nlohmann/json/issues/2089> for more information.
-
-#if __cplusplus <= 201703L
- #include <ciso646> // and, not, or
-#endif
-
// #include <nlohmann/detail/exceptions.hpp>
#include <exception> // exception
#include <stdexcept> // runtime_error
#include <string> // to_string
+#include <vector> // vector
-// #include <nlohmann/detail/input/position_t.hpp>
+// #include <nlohmann/detail/value_t.hpp>
+#include <array> // array
#include <cstddef> // size_t
+#include <cstdint> // uint8_t
+#include <string> // string
namespace nlohmann
{
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;
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
- /// conversion to size_t to preserve SAX interface
- constexpr operator size_t() const
- {
- return chars_read_total;
- }
+/*!
+@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
};
-} // namespace detail
-} // namespace nlohmann
+/*!
+@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
+*/
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+ 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);
+ return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
+}
+} // namespace detail
+} // namespace nlohmann
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+#include <string>
// #include <nlohmann/detail/macro_scope.hpp>
#include <utility> // pair
// #include <nlohmann/thirdparty/hedley/hedley.hpp>
+
+
/* Hedley - https://nemequ.github.io/hedley
* Created by Evan Nemerson <evan@nemerson.com>
*
@@ -130,11 +181,11 @@ struct position_t
* SPDX-License-Identifier: CC0-1.0
*/
-#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 13)
+#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)
#if defined(JSON_HEDLEY_VERSION)
#undef JSON_HEDLEY_VERSION
#endif
-#define JSON_HEDLEY_VERSION 13
+#define JSON_HEDLEY_VERSION 15
#if defined(JSON_HEDLEY_STRINGIFY_EX)
#undef JSON_HEDLEY_STRINGIFY_EX
@@ -207,18 +258,18 @@ struct position_t
#if defined(JSON_HEDLEY_MSVC_VERSION)
#undef JSON_HEDLEY_MSVC_VERSION
#endif
-#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000)
+#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)
#define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)
-#elif defined(_MSC_FULL_VER)
+#elif defined(_MSC_FULL_VER) && !defined(__ICL)
#define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)
-#elif defined(_MSC_VER)
+#elif defined(_MSC_VER) && !defined(__ICL)
#define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)
#endif
#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)
#undef JSON_HEDLEY_MSVC_VERSION_CHECK
#endif
-#if !defined(_MSC_VER)
+#if !defined(JSON_HEDLEY_MSVC_VERSION)
#define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)
#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
#define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
@@ -231,9 +282,9 @@ struct position_t
#if defined(JSON_HEDLEY_INTEL_VERSION)
#undef JSON_HEDLEY_INTEL_VERSION
#endif
-#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE)
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)
#define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)
-#elif defined(__INTEL_COMPILER)
+#elif defined(__INTEL_COMPILER) && !defined(__ICL)
#define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
#endif
@@ -246,6 +297,22 @@ struct position_t
#define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)
#endif
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+ #undef JSON_HEDLEY_INTEL_CL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)
+ #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)
+ #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+ #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
#if defined(JSON_HEDLEY_PGI_VERSION)
#undef JSON_HEDLEY_PGI_VERSION
#endif
@@ -485,7 +552,7 @@ struct position_t
#if __VER__ > 1000
#define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))
#else
- #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0)
+ #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)
#endif
#endif
@@ -562,6 +629,22 @@ struct position_t
#define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)
#endif
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+ #undef JSON_HEDLEY_MCST_LCC_VERSION
+#endif
+#if defined(__LCC__) && defined(__LCC_MINOR__)
+ #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)
+ #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+ #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
#if defined(JSON_HEDLEY_GCC_VERSION)
#undef JSON_HEDLEY_GCC_VERSION
#endif
@@ -571,6 +654,7 @@ struct position_t
!defined(JSON_HEDLEY_INTEL_VERSION) && \
!defined(JSON_HEDLEY_PGI_VERSION) && \
!defined(JSON_HEDLEY_ARM_VERSION) && \
+ !defined(JSON_HEDLEY_CRAY_VERSION) && \
!defined(JSON_HEDLEY_TI_VERSION) && \
!defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \
!defined(JSON_HEDLEY_TI_CL430_VERSION) && \
@@ -578,7 +662,8 @@ struct position_t
!defined(JSON_HEDLEY_TI_CL6X_VERSION) && \
!defined(JSON_HEDLEY_TI_CL7X_VERSION) && \
!defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \
- !defined(__COMPCERT__)
+ !defined(__COMPCERT__) && \
+ !defined(JSON_HEDLEY_MCST_LCC_VERSION)
#define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION
#endif
@@ -594,17 +679,21 @@ struct position_t
#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)
#undef JSON_HEDLEY_HAS_ATTRIBUTE
#endif
-#if defined(__has_attribute)
- #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)
+#if \
+ defined(__has_attribute) && \
+ ( \
+ (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \
+ )
+# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)
#else
- #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)
+# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)
#endif
#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)
#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
#endif
#if defined(__has_attribute)
- #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)
+ #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
#else
#define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
#endif
@@ -613,7 +702,7 @@ struct position_t
#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
#endif
#if defined(__has_attribute)
- #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)
+ #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
#else
#define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
#endif
@@ -798,6 +887,72 @@ struct position_t
#define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
#endif
+#if \
+ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+ defined(__clang__) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \
+ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \
+ (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))
+ #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+ #define JSON_HEDLEY_PRAGMA(value) __pragma(value)
+#else
+ #define JSON_HEDLEY_PRAGMA(value)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)
+ #undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#endif
+#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)
+ #undef JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+#if defined(__clang__)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))
+ #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))
+#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop")
+#elif \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH
+ #define JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+
/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for
HEDLEY INTERNAL USE ONLY. API subject to change without notice. */
#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
@@ -806,12 +961,22 @@ struct position_t
#if defined(__cplusplus)
# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat")
# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions")
-# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions")
+# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
JSON_HEDLEY_DIAGNOSTIC_PUSH \
_Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
_Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+ _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \
xpr \
JSON_HEDLEY_DIAGNOSTIC_POP
+# else
+# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+ _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+ xpr \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# endif
# else
# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
JSON_HEDLEY_DIAGNOSTIC_PUSH \
@@ -876,7 +1041,7 @@ struct position_t
# define JSON_HEDLEY_CPP_CAST(T, expr) \
JSON_HEDLEY_DIAGNOSTIC_PUSH \
_Pragma("diag_suppress=Pe137") \
- JSON_HEDLEY_DIAGNOSTIC_POP \
+ JSON_HEDLEY_DIAGNOSTIC_POP
# else
# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))
# endif
@@ -884,70 +1049,6 @@ struct position_t
# define JSON_HEDLEY_CPP_CAST(T, expr) (expr)
#endif
-#if \
- (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
- defined(__clang__) || \
- JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
- JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
- JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
- JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
- JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
- JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
- JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
- JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
- JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
- JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \
- JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
- JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \
- JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \
- JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \
- (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))
- #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)
-#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
- #define JSON_HEDLEY_PRAGMA(value) __pragma(value)
-#else
- #define JSON_HEDLEY_PRAGMA(value)
-#endif
-
-#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)
- #undef JSON_HEDLEY_DIAGNOSTIC_PUSH
-#endif
-#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)
- #undef JSON_HEDLEY_DIAGNOSTIC_POP
-#endif
-#if defined(__clang__)
- #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push")
- #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")
-#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
- #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
- #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
-#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
- #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
- #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
-#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
- #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))
- #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))
-#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)
- #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push")
- #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop")
-#elif \
- JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
- JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
- JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \
- JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
- JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
- #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push")
- #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop")
-#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
- #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
- #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
-#else
- #define JSON_HEDLEY_DIAGNOSTIC_PUSH
- #define JSON_HEDLEY_DIAGNOSTIC_POP
-#endif
-
#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
#endif
@@ -955,12 +1056,18 @@ struct position_t
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445")
#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
#elif \
JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
@@ -993,6 +1100,8 @@ struct position_t
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"")
#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))
#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675")
#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
@@ -1009,6 +1118,8 @@ struct position_t
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161")
#else
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
#endif
@@ -1022,8 +1133,12 @@ struct position_t
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))
#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098")
#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)
@@ -1035,6 +1150,8 @@ struct position_t
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173")
#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
#else
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
#endif
@@ -1052,20 +1169,34 @@ struct position_t
#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
#endif
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunused-function")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+
#if defined(JSON_HEDLEY_DEPRECATED)
#undef JSON_HEDLEY_DEPRECATED
#endif
#if defined(JSON_HEDLEY_DEPRECATED_FOR)
#undef JSON_HEDLEY_DEPRECATED_FOR
#endif
-#if JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0)
+#if \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
#define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since))
#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement))
-#elif defined(__cplusplus) && (__cplusplus >= 201402L)
- #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]])
- #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]])
#elif \
- JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \
+ (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
@@ -1075,9 +1206,13 @@ struct position_t
JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since)))
#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement)))
+#elif defined(__cplusplus) && (__cplusplus >= 201402L)
+ #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]])
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]])
#elif \
JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \
JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
@@ -1092,12 +1227,15 @@ struct position_t
(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
#define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))
#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))
#elif \
JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
- JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0)
+ JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
#define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)
#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)
#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
@@ -1114,7 +1252,8 @@ struct position_t
#if \
JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \
JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \
- JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since)))
#else
#define JSON_HEDLEY_UNAVAILABLE(available_since)
@@ -1126,13 +1265,7 @@ struct position_t
#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)
#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
#endif
-#if (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)
- #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
- #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])
-#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)
- #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
- #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
-#elif \
+#if \
JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \
JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
@@ -1148,9 +1281,16 @@ struct position_t
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
(JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
- JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))
+#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
#elif defined(_Check_return_) /* SAL */
#define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_
#define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_
@@ -1166,7 +1306,8 @@ struct position_t
JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \
JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
- JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0)
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))
#else
#define JSON_HEDLEY_SENTINEL(position)
@@ -1177,7 +1318,9 @@ struct position_t
#endif
#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
#define JSON_HEDLEY_NO_RETURN __noreturn
-#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+#elif \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define JSON_HEDLEY_NO_RETURN _Noreturn
@@ -1199,11 +1342,14 @@ struct position_t
(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
#define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
#define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return")
-#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0)
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
#define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
#define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;")
@@ -1235,7 +1381,8 @@ struct position_t
#endif
#if \
JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
- JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
#define JSON_HEDLEY_ASSUME(expr) __assume(expr)
#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)
#define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)
@@ -1253,7 +1400,9 @@ struct position_t
JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \
JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
- JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5)
+ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()
#elif defined(JSON_HEDLEY_ASSUME)
#define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
@@ -1331,7 +1480,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))
#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)
#define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))
@@ -1367,15 +1517,16 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))
#endif
#if \
- JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \
- JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0)
+ (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability))
# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability))
# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability))
# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 )
# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 )
#elif \
- JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \
+ (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
(JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
@@ -1389,7 +1540,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \
- JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0)
+ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
# define JSON_HEDLEY_PREDICT(expr, expected, probability) \
(((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))
# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \
@@ -1435,11 +1587,14 @@ JSON_HEDLEY_DIAGNOSTIC_POP
(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_MALLOC __attribute__((__malloc__))
#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
#define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory")
-#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0)
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
#define JSON_HEDLEY_MALLOC __declspec(restrict)
#else
#define JSON_HEDLEY_MALLOC
@@ -1466,7 +1621,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
- JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
# define JSON_HEDLEY_PURE __attribute__((__pure__))
#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data")
@@ -1502,7 +1658,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
- JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_CONST __attribute__((__const__))
#elif \
JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
@@ -1520,6 +1677,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP
JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
@@ -1529,7 +1687,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
(JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \
JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
- defined(__clang__)
+ defined(__clang__) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_RESTRICT __restrict
#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)
#define JSON_HEDLEY_RESTRICT _Restrict
@@ -1550,13 +1709,15 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#define JSON_HEDLEY_INLINE __inline__
#elif \
JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \
JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_INLINE __inline
#else
#define JSON_HEDLEY_INLINE
@@ -1582,9 +1743,13 @@ JSON_HEDLEY_DIAGNOSTIC_POP
(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE
-#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0)
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
# define JSON_HEDLEY_ALWAYS_INLINE __forceinline
#elif defined(__cplusplus) && \
( \
@@ -1622,9 +1787,13 @@ JSON_HEDLEY_DIAGNOSTIC_POP
(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
- JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
#define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))
-#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0)
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
#define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)
#define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline")
@@ -1667,7 +1836,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
(JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \
) \
- )
+ ) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden")))
# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default")))
# else
@@ -1683,10 +1853,12 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#if \
JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \
JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
- JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))
#elif \
JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
#define JSON_HEDLEY_NO_THROW __declspec(nothrow)
#else
@@ -1698,7 +1870,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#endif
#if \
JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \
- JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0)
+ JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))
#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)
#define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])
@@ -1715,7 +1888,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#endif
#if \
JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \
- JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))
#elif defined(_Ret_notnull_) /* SAL */
#define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_
@@ -1757,7 +1931,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
(JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \
- JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0)
+ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
#define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)
#endif
#if !defined(__cplusplus)
@@ -1781,7 +1956,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP
!defined(JSON_HEDLEY_SUNPRO_VERSION) && \
!defined(JSON_HEDLEY_PGI_VERSION) && \
!defined(JSON_HEDLEY_IAR_VERSION)) || \
- JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \
+ (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \
JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
@@ -1851,7 +2026,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#if \
!defined(__cplusplus) && ( \
(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \
- JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \
+ (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \
JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
defined(_Static_assert) \
@@ -1859,7 +2034,8 @@ JSON_HEDLEY_DIAGNOSTIC_POP
# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)
#elif \
(defined(__cplusplus) && (__cplusplus >= 201103L)) || \
- JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0)
+ JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))
#else
# define JSON_HEDLEY_STATIC_ASSERT(expr, message)
@@ -1919,7 +2095,9 @@ JSON_HEDLEY_DIAGNOSTIC_POP
JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)
-#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))
#else
# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)
@@ -1955,8 +2133,10 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#if defined(JSON_HEDLEY_FLAGS)
#undef JSON_HEDLEY_FLAGS
#endif
-#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum)
+#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion"))
#define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))
+#else
+ #define JSON_HEDLEY_FLAGS
#endif
#if defined(JSON_HEDLEY_FLAGS_CAST)
@@ -1976,7 +2156,9 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#if defined(JSON_HEDLEY_EMPTY_BASES)
#undef JSON_HEDLEY_EMPTY_BASES
#endif
-#if JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)
+#if \
+ (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
#define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)
#else
#define JSON_HEDLEY_EMPTY_BASES
@@ -2048,17 +2230,20 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#endif
// C++ language standard detection
-#if (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
-
-// disable float-equal warnings on GCC/clang
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wfloat-equal"
+// 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
// disable documentation warnings on clang
@@ -2101,6 +2286,19 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
#endif
+// allow to override 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
@@ -2149,6 +2347,261 @@ JSON_HEDLEY_DIAGNOSTIC_POP
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
AllocatorType, JSONSerializer, BinaryType>
+// 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);
+
+/*!
+@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__)) }
+
+/*!
+@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__)) }
+
+#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
+
+
+namespace nlohmann
+{
+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
+*/
+inline void replace_substring(std::string& s, const std::string& f,
+ const std::string& t)
+{
+ JSON_ASSERT(!f.empty());
+ for (auto pos = s.find(f); // find first occurrence of f
+ pos != std::string::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.
+ */
+inline std::string escape(std::string s)
+{
+ replace_substring(s, "~", "~0");
+ replace_substring(s, "/", "~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.
+ */
+static void unescape(std::string& s)
+{
+ replace_substring(s, "~1", "/");
+ replace_substring(s, "~0", "~");
+}
+
+} // namespace detail
+} // namespace nlohmann
+
+// #include <nlohmann/detail/input/position_t.hpp>
+
+
+#include <cstddef> // size_t
+
+namespace nlohmann
+{
+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
+} // namespace nlohmann
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
namespace nlohmann
{
@@ -2190,14 +2643,13 @@ class exception : public std::exception
{
public:
/// returns the explanatory string
- JSON_HEDLEY_RETURNS_NON_NULL
const char* what() const noexcept override
{
return m.what();
}
/// the id of the exception
- const int id;
+ const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
protected:
JSON_HEDLEY_NON_NULL(3)
@@ -2208,6 +2660,62 @@ class exception : public std::exception
return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
}
+ 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->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_value.array->size(); ++i)
+ {
+ if (&current->m_parent->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_value.object)
+ {
+ if (&element.second == current)
+ {
+ tokens.emplace_back(element.first.c_str());
+ break;
+ }
+ }
+ break;
+ }
+
+ default: // LCOV_EXCL_LINE
+ break; // LCOV_EXCL_LINE
+ }
+ }
+
+ if (tokens.empty())
+ {
+ return "";
+ }
+
+ return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
+ [](const std::string & a, const std::string & b)
+ {
+ return a + "/" + detail::escape(b);
+ }) + ") ";
+#else
+ static_cast<void>(leaf_element);
+ return "";
+#endif
+ }
+
private:
/// an exception object as storage for error messages
std::runtime_error m;
@@ -2240,6 +2748,7 @@ json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vect
json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.
json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.
json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet).
+json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed.
@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
@@ -2269,18 +2778,20 @@ class parse_error : public exception
@param[in] what_arg the explanatory string
@return parse_error object
*/
- static parse_error create(int id_, const position_t& pos, const std::string& what_arg)
+ template<typename BasicJsonType>
+ static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
- position_string(pos) + ": " + what_arg;
+ position_string(pos) + ": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, pos.chars_read_total, w.c_str());
}
- static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)
+ template<typename BasicJsonType>
+ static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
(byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") +
- ": " + what_arg;
+ ": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, byte_, w.c_str());
}
@@ -2346,9 +2857,10 @@ caught.,invalid_iterator}
class invalid_iterator : public exception
{
public:
- static invalid_iterator create(int id_, const std::string& what_arg)
+ template<typename BasicJsonType>
+ static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
- std::string w = exception::name("invalid_iterator", id_) + what_arg;
+ std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg;
return invalid_iterator(id_, w.c_str());
}
@@ -2400,9 +2912,10 @@ caught.,type_error}
class type_error : public exception
{
public:
- static type_error create(int id_, const std::string& what_arg)
+ template<typename BasicJsonType>
+ static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
- std::string w = exception::name("type_error", id_) + what_arg;
+ std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg;
return type_error(id_, w.c_str());
}
@@ -2428,7 +2941,7 @@ json.exception.out_of_range.403 | key 'foo' not found | The provided key was not
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
-json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. |
+json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) |
json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |
json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |
@@ -2447,9 +2960,10 @@ caught.,out_of_range}
class out_of_range : public exception
{
public:
- static out_of_range create(int id_, const std::string& what_arg)
+ template<typename BasicJsonType>
+ static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
- std::string w = exception::name("out_of_range", id_) + what_arg;
+ std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg;
return out_of_range(id_, w.c_str());
}
@@ -2485,9 +2999,10 @@ caught.,other_error}
class other_error : public exception
{
public:
- static other_error create(int id_, const std::string& what_arg)
+ template<typename BasicJsonType>
+ static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
- std::string w = exception::name("other_error", id_) + what_arg;
+ std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg;
return other_error(id_, w.c_str());
}
@@ -2505,52 +3020,141 @@ class other_error : public exception
#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/boolean_operators.hpp>
+// #include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
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;
-template<typename T>
-using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::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
-// implementation of C++14 index_sequence and affiliates
-// source: https://stackoverflow.com/a/32223343
-template<std::size_t... Ints>
-struct index_sequence
+// 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 type = index_sequence;
- using value_type = std::size_t;
+ using value_type = T;
static constexpr std::size_t size() noexcept
{
return sizeof...(Ints);
}
};
-template<class Sequence1, class Sequence2>
-struct merge_and_renumber;
+// 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<std::size_t... I1, std::size_t... I2>
-struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
- : index_sequence < I1..., (sizeof...(I1) + I2)... > {};
+template <typename Seq, size_t SeqSize, size_t Rem>
+struct Extend;
-template<std::size_t N>
-struct make_index_sequence
- : merge_and_renumber < typename make_index_sequence < N / 2 >::type,
- typename make_index_sequence < N - N / 2 >::type > {};
+// 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<> struct make_index_sequence<0> : index_sequence<> {};
-template<> struct make_index_sequence<1> : index_sequence<0> {};
+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... Ts>
+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> {};
@@ -2564,6 +3168,19 @@ struct static_const
template<typename T>
constexpr T static_const<T>::value;
+
+} // namespace detail
+} // namespace nlohmann
+
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
+// dispatching helper struct
+template <class T> struct identity_tag {};
} // namespace detail
} // namespace nlohmann
@@ -2573,8 +3190,7 @@ constexpr T static_const<T>::value;
#include <limits> // numeric_limits
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
#include <utility> // declval
-
-// #include <nlohmann/detail/boolean_operators.hpp>
+#include <tuple> // tuple
// #include <nlohmann/detail/iterators/iterator_traits.hpp>
@@ -2588,11 +3204,11 @@ namespace nlohmann
{
namespace detail
{
-template <typename ...Ts> struct make_void
+template<typename ...Ts> struct make_void
{
using type = void;
};
-template <typename ...Ts> using void_t = typename make_void<Ts...>::type;
+template<typename ...Ts> using void_t = typename make_void<Ts...>::type;
} // namespace detail
} // namespace nlohmann
@@ -2603,10 +3219,10 @@ namespace nlohmann
{
namespace detail
{
-template <typename It, typename = void>
+template<typename It, typename = void>
struct iterator_types {};
-template <typename It>
+template<typename It>
struct iterator_types <
It,
void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
@@ -2621,18 +3237,18 @@ struct iterator_types <
// 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>
+template<typename T, typename = void>
struct iterator_traits
{
};
-template <typename T>
+template<typename T>
struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
: iterator_types<T>
{
};
-template <typename T>
+template<typename T>
struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
{
using iterator_category = std::random_access_iterator_tag;
@@ -2671,39 +3287,39 @@ struct nonesuch
void operator=(nonesuch&&) = delete;
};
-template <class Default,
- class AlwaysVoid,
- template <class...> class Op,
- class... Args>
+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>
+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>
+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>
+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>
+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>
+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>
+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>
+template<class To, template<class...> class Op, class... Args>
using is_detected_convertible =
std::is_convertible<detected_t<Op, Args...>, To>;
} // namespace detail
@@ -2772,6 +3388,19 @@ uses the standard template types.
@since version 1.0.0
*/
using json = basic_json<>;
+
+template<class Key, class T, class IgnoredLess, class Allocator>
+struct ordered_map;
+
+/*!
+@brief ordered JSON class
+
+This type preserves the insertion order of object keys.
+
+@since version 3.9.0
+*/
+using ordered_json = basic_json<nlohmann::ordered_map>;
+
} // namespace nlohmann
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
@@ -2811,59 +3440,68 @@ struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
// json_ref helpers //
//////////////////////
-template <typename>
+template<typename>
class json_ref;
template<typename>
struct is_json_ref : std::false_type {};
-template <typename T>
+template<typename T>
struct is_json_ref<json_ref<T>> : std::true_type {};
//////////////////////////
// aliases for detected //
//////////////////////////
-template <typename T>
+template<typename T>
using mapped_type_t = typename T::mapped_type;
-template <typename T>
+template<typename T>
using key_type_t = typename T::key_type;
-template <typename T>
+template<typename T>
using value_type_t = typename T::value_type;
-template <typename T>
+template<typename T>
using difference_type_t = typename T::difference_type;
-template <typename T>
+template<typename T>
using pointer_t = typename T::pointer;
-template <typename T>
+template<typename T>
using reference_t = typename T::reference;
-template <typename T>
+template<typename T>
using iterator_category_t = typename T::iterator_category;
-template <typename T>
+template<typename T>
using iterator_t = typename T::iterator;
-template <typename T, typename... Args>
+template<typename T, typename... Args>
using to_json_function = decltype(T::to_json(std::declval<Args>()...));
-template <typename T, typename... Args>
+template<typename T, typename... Args>
using from_json_function = decltype(T::from_json(std::declval<Args>()...));
-template <typename T, typename U>
+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>
+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 has_from_json<BasicJsonType, T,
- enable_if_t<not is_basic_json<T>::value>>
+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>;
@@ -2874,11 +3512,11 @@ struct has_from_json<BasicJsonType, T,
// 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>
+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<not is_basic_json<T>::value>>
+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>;
@@ -2889,11 +3527,11 @@ struct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json
// 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>
+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<not is_basic_json<T>::value>>
+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>;
@@ -2907,10 +3545,56 @@ struct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>>
// is_ functions //
///////////////////
-template <typename T, typename = void>
-struct is_iterator_traits : std::false_type {};
+// https://en.cppreference.com/w/cpp/types/conjunction
+template<class...> struct conjunction : std::true_type { };
+template<class B1> struct conjunction<B1> : B1 { };
+template<class B1, class... Bn>
+struct conjunction<B1, Bn...>
+: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
+// 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:
@@ -2925,154 +3609,155 @@ struct is_iterator_traits<iterator_traits<T>>
is_detected<reference_t, traits>::value;
};
-// source: https://stackoverflow.com/a/37193089/4116453
+// 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>
+template<typename T, typename = void>
struct is_complete_type : std::false_type {};
-template <typename T>
+template<typename T>
struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
-template <typename BasicJsonType, typename CompatibleObjectType,
- typename = void>
+template<typename BasicJsonType, typename CompatibleObjectType,
+ typename = void>
struct is_compatible_object_type_impl : std::false_type {};
-template <typename BasicJsonType, typename CompatibleObjectType>
+template<typename BasicJsonType, typename CompatibleObjectType>
struct is_compatible_object_type_impl <
BasicJsonType, CompatibleObjectType,
- enable_if_t<is_detected<mapped_type_t, CompatibleObjectType>::value and
+ 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 =
- std::is_constructible<typename object_t::key_type,
- typename CompatibleObjectType::key_type>::value and
- std::is_constructible<typename object_t::mapped_type,
+ 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>
+template<typename BasicJsonType, typename CompatibleObjectType>
struct is_compatible_object_type
: is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
-template <typename BasicJsonType, typename ConstructibleObjectType,
- typename = void>
+template<typename BasicJsonType, typename ConstructibleObjectType,
+ typename = void>
struct is_constructible_object_type_impl : std::false_type {};
-template <typename BasicJsonType, typename ConstructibleObjectType>
+template<typename BasicJsonType, typename ConstructibleObjectType>
struct is_constructible_object_type_impl <
BasicJsonType, ConstructibleObjectType,
- enable_if_t<is_detected<mapped_type_t, ConstructibleObjectType>::value and
+ 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 =
- (std::is_default_constructible<ConstructibleObjectType>::value and
- (std::is_move_assignable<ConstructibleObjectType>::value or
- std::is_copy_assignable<ConstructibleObjectType>::value) and
- (std::is_constructible<typename ConstructibleObjectType::key_type,
- typename object_t::key_type>::value and
+ (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)) or
+ typename ConstructibleObjectType::mapped_type >::value)) ||
(has_from_json<BasicJsonType,
- typename ConstructibleObjectType::mapped_type>::value or
+ typename ConstructibleObjectType::mapped_type>::value ||
has_non_default_from_json <
BasicJsonType,
typename ConstructibleObjectType::mapped_type >::value);
};
-template <typename BasicJsonType, typename ConstructibleObjectType>
+template<typename BasicJsonType, typename ConstructibleObjectType>
struct is_constructible_object_type
: is_constructible_object_type_impl<BasicJsonType,
ConstructibleObjectType> {};
-template <typename BasicJsonType, typename CompatibleStringType,
- typename = void>
+template<typename BasicJsonType, typename CompatibleStringType,
+ typename = void>
struct is_compatible_string_type_impl : std::false_type {};
-template <typename BasicJsonType, typename CompatibleStringType>
+template<typename BasicJsonType, typename CompatibleStringType>
struct is_compatible_string_type_impl <
BasicJsonType, CompatibleStringType,
enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,
value_type_t, CompatibleStringType>::value >>
{
static constexpr auto value =
- std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
+ is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
};
-template <typename BasicJsonType, typename ConstructibleStringType>
+template<typename BasicJsonType, typename ConstructibleStringType>
struct is_compatible_string_type
: is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
-template <typename BasicJsonType, typename ConstructibleStringType,
- typename = void>
+template<typename BasicJsonType, typename ConstructibleStringType,
+ typename = void>
struct is_constructible_string_type_impl : std::false_type {};
-template <typename BasicJsonType, typename ConstructibleStringType>
+template<typename BasicJsonType, typename ConstructibleStringType>
struct is_constructible_string_type_impl <
BasicJsonType, ConstructibleStringType,
enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,
value_type_t, ConstructibleStringType>::value >>
{
static constexpr auto value =
- std::is_constructible<ConstructibleStringType,
+ is_constructible<ConstructibleStringType,
typename BasicJsonType::string_t>::value;
};
-template <typename BasicJsonType, typename ConstructibleStringType>
+template<typename BasicJsonType, typename ConstructibleStringType>
struct is_constructible_string_type
: is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
-template <typename BasicJsonType, typename CompatibleArrayType, typename = void>
+template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
struct is_compatible_array_type_impl : std::false_type {};
-template <typename BasicJsonType, typename CompatibleArrayType>
+template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type_impl <
BasicJsonType, CompatibleArrayType,
- enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and
- is_detected<iterator_t, CompatibleArrayType>::value and
+ enable_if_t < is_detected<value_type_t, CompatibleArrayType>::value&&
+ is_detected<iterator_t, CompatibleArrayType>::value&&
// This is needed because json_reverse_iterator has a ::iterator type...
// Therefore it is detected as a CompatibleArrayType.
// The real fix would be to have an Iterable concept.
- not is_iterator_traits<
- iterator_traits<CompatibleArrayType>>::value >>
+ !is_iterator_traits <
+ iterator_traits<CompatibleArrayType >>::value >>
{
static constexpr bool value =
- std::is_constructible<BasicJsonType,
+ is_constructible<BasicJsonType,
typename CompatibleArrayType::value_type>::value;
};
-template <typename BasicJsonType, typename CompatibleArrayType>
+template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type
: is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
-template <typename BasicJsonType, typename ConstructibleArrayType, typename = void>
+template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
struct is_constructible_array_type_impl : std::false_type {};
-template <typename BasicJsonType, typename ConstructibleArrayType>
+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>
+template<typename BasicJsonType, typename ConstructibleArrayType>
struct is_constructible_array_type_impl <
BasicJsonType, ConstructibleArrayType,
- enable_if_t<not std::is_same<ConstructibleArrayType,
- typename BasicJsonType::value_type>::value and
- std::is_default_constructible<ConstructibleArrayType>::value and
-(std::is_move_assignable<ConstructibleArrayType>::value or
- std::is_copy_assignable<ConstructibleArrayType>::value) and
-is_detected<value_type_t, ConstructibleArrayType>::value and
-is_detected<iterator_t, ConstructibleArrayType>::value and
-is_complete_type<
-detected_t<value_type_t, ConstructibleArrayType>>::value >>
+ enable_if_t < !std::is_same<ConstructibleArrayType,
+ typename BasicJsonType::value_type>::value&&
+ is_default_constructible<ConstructibleArrayType>::value&&
+(std::is_move_assignable<ConstructibleArrayType>::value ||
+ std::is_copy_assignable<ConstructibleArrayType>::value)&&
+is_detected<value_type_t, ConstructibleArrayType>::value&&
+is_detected<iterator_t, ConstructibleArrayType>::value&&
+is_complete_type <
+detected_t<value_type_t, ConstructibleArrayType >>::value >>
{
static constexpr bool value =
// This is needed because json_reverse_iterator has a ::iterator type,
@@ -3080,51 +3765,51 @@ detected_t<value_type_t, ConstructibleArrayType>>::value >>
// base class `iterator`... Therefore it is detected as a
// ConstructibleArrayType. The real fix would be to have an Iterable
// concept.
- not is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value and
+ !is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value &&
(std::is_same<typename ConstructibleArrayType::value_type,
- typename BasicJsonType::array_t::value_type>::value or
+ typename BasicJsonType::array_t::value_type>::value ||
has_from_json<BasicJsonType,
- typename ConstructibleArrayType::value_type>::value or
+ typename ConstructibleArrayType::value_type>::value ||
has_non_default_from_json <
BasicJsonType, typename ConstructibleArrayType::value_type >::value);
};
-template <typename BasicJsonType, typename ConstructibleArrayType>
+template<typename BasicJsonType, typename ConstructibleArrayType>
struct is_constructible_array_type
: is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
-template <typename RealIntegerType, typename CompatibleNumberIntegerType,
- typename = void>
+template<typename RealIntegerType, typename CompatibleNumberIntegerType,
+ typename = void>
struct is_compatible_integer_type_impl : std::false_type {};
-template <typename RealIntegerType, typename CompatibleNumberIntegerType>
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
struct is_compatible_integer_type_impl <
RealIntegerType, CompatibleNumberIntegerType,
- enable_if_t<std::is_integral<RealIntegerType>::value and
- std::is_integral<CompatibleNumberIntegerType>::value and
- not std::is_same<bool, CompatibleNumberIntegerType>::value >>
+ 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 =
- std::is_constructible<RealIntegerType,
- CompatibleNumberIntegerType>::value and
- CompatibleLimits::is_integer and
+ is_constructible<RealIntegerType,
+ CompatibleNumberIntegerType>::value &&
+ CompatibleLimits::is_integer &&
RealLimits::is_signed == CompatibleLimits::is_signed;
};
-template <typename RealIntegerType, typename CompatibleNumberIntegerType>
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
struct is_compatible_integer_type
: is_compatible_integer_type_impl<RealIntegerType,
CompatibleNumberIntegerType> {};
-template <typename BasicJsonType, typename CompatibleType, typename = void>
+template<typename BasicJsonType, typename CompatibleType, typename = void>
struct is_compatible_type_impl: std::false_type {};
-template <typename BasicJsonType, typename CompatibleType>
+template<typename BasicJsonType, typename CompatibleType>
struct is_compatible_type_impl <
BasicJsonType, CompatibleType,
enable_if_t<is_complete_type<CompatibleType>::value >>
@@ -3133,112 +3818,21 @@ struct is_compatible_type_impl <
has_to_json<BasicJsonType, CompatibleType>::value;
};
-template <typename BasicJsonType, typename CompatibleType>
+template<typename BasicJsonType, typename CompatibleType>
struct is_compatible_type
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
-// https://en.cppreference.com/w/cpp/types/conjunction
-template<class...> struct conjunction : std::true_type { };
-template<class B1> struct conjunction<B1> : B1 { };
-template<class B1, class... Bn>
-struct conjunction<B1, Bn...>
-: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
-
-template <typename T1, typename T2>
+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<std::is_constructible<T1, Args>...> {};
+template<typename T1, typename... Args>
+struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};
} // namespace detail
} // namespace nlohmann
// #include <nlohmann/detail/value_t.hpp>
-#include <array> // array
-#include <cstddef> // size_t
-#include <cstdint> // uint8_t
-#include <string> // string
-
-// #include <nlohmann/detail/boolean_operators.hpp>
-
-
-namespace nlohmann
-{
-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 @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
-*/
-inline bool operator<(const value_t lhs, const value_t rhs) noexcept
-{
- 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);
- return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];
-}
-} // namespace detail
-} // namespace nlohmann
-
-
namespace nlohmann
{
namespace detail
@@ -3246,18 +3840,18 @@ namespace detail
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_null()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{
- JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(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 and
- not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
- int> = 0>
+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))
@@ -3279,16 +3873,16 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
}
default:
- JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j));
}
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_boolean()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{
- JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j));
}
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
}
@@ -3296,9 +3890,9 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_string()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
- JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
@@ -3306,15 +3900,15 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
template <
typename BasicJsonType, typename ConstructibleStringType,
enable_if_t <
- is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value and
- not std::is_same<typename BasicJsonType::string_t,
- ConstructibleStringType>::value,
+ is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&&
+ !std::is_same<typename BasicJsonType::string_t,
+ ConstructibleStringType>::value,
int > = 0 >
void from_json(const BasicJsonType& j, ConstructibleStringType& s)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_string()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
- JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
@@ -3349,12 +3943,12 @@ void from_json(const BasicJsonType& j, EnumType& e)
// forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator,
- enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
+ enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
l.clear();
std::transform(j.rbegin(), j.rend(),
@@ -3366,19 +3960,23 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
// valarray doesn't have an insert method
template<typename BasicJsonType, typename T,
- enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
+ enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::valarray<T>& l)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
l.resize(j.size());
- std::copy(j.begin(), j.end(), std::begin(l));
+ 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])
+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)
@@ -3393,7 +3991,7 @@ void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
}
-template <typename BasicJsonType, typename T, std::size_t N>
+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())
@@ -3404,7 +4002,10 @@ auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
}
}
-template<typename BasicJsonType, typename ConstructibleArrayType>
+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>()),
@@ -3425,7 +4026,10 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
arr = std::move(ret);
}
-template <typename BasicJsonType, typename ConstructibleArrayType>
+template<typename BasicJsonType, typename ConstructibleArrayType,
+ enable_if_t<
+ std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+ int> = 0>
void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
priority_tag<0> /*unused*/)
{
@@ -3443,34 +4047,52 @@ void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
arr = std::move(ret);
}
-template <typename BasicJsonType, typename ConstructibleArrayType,
- enable_if_t <
- is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value and
- not is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value and
- not is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value and
- not std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value and
- not is_basic_json<ConstructibleArrayType>::value,
- int > = 0 >
+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(not j.is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
- JSON_THROW(type_error::create(302, "type must be array, but is " +
- std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
from_json_array_impl(j, arr, priority_tag<3> {});
}
-template <typename BasicJsonType>
+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, "type must be array, but is " + std::string(j.type_name()), j));
+ }
+
+ return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
+}
+
+template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_binary()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{
- JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j));
}
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
@@ -3480,13 +4102,13 @@ template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_object()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{
- JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j));
}
ConstructibleObjectType ret;
- auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+ 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(),
@@ -3502,14 +4124,14 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
// (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 and
- not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
- not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
- not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
- not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
- int> = 0>
+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 >
void from_json(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
@@ -3536,63 +4158,88 @@ void from_json(const BasicJsonType& j, ArithmeticType& val)
}
default:
- JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(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>
-void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
+void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
{
- p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
+ p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
}
-template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
-void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...> /*unused*/)
+template<typename BasicJsonType, typename... Args>
+std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
{
- t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
+ return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
}
template<typename BasicJsonType, typename... Args>
-void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
+void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
{
- from_json_tuple_impl(j, t, index_sequence_for<Args...> {});
+ t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
}
-template <typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
- typename = enable_if_t<not std::is_constructible<
- typename BasicJsonType::string_t, Key>::value>>
+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, "type must be array, but is " + std::string(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 >>
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
m.clear();
for (const auto& p : j)
{
- if (JSON_HEDLEY_UNLIKELY(not p.is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name())));
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(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<not std::is_constructible<
- typename BasicJsonType::string_t, Key>::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 >>
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{
- if (JSON_HEDLEY_UNLIKELY(not j.is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
m.clear();
for (const auto& p : j)
{
- if (JSON_HEDLEY_UNLIKELY(not p.is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{
- JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name())));
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
@@ -3601,11 +4248,11 @@ void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyE
struct from_json_fn
{
template<typename BasicJsonType, typename T>
- auto operator()(const BasicJsonType& j, T& val) const
- noexcept(noexcept(from_json(j, val)))
- -> decltype(from_json(j, val), void())
+ 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, val);
+ return from_json(j, std::forward<T>(val));
}
};
} // namespace detail
@@ -3613,9 +4260,9 @@ struct from_json_fn
/// 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
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
{
-constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
+constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; // NOLINT(misc-definitions-in-headers)
} // namespace
} // namespace nlohmann
@@ -3631,8 +4278,6 @@ constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::va
#include <valarray> // valarray
#include <vector> // vector
-// #include <nlohmann/detail/boolean_operators.hpp>
-
// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
@@ -3640,6 +4285,7 @@ constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::va
#include <iterator> // input_iterator_tag
#include <string> // string, to_string
#include <tuple> // tuple_size, get, tuple_element
+#include <utility> // move
// #include <nlohmann/detail/meta/type_traits.hpp>
@@ -3653,9 +4299,11 @@ namespace detail
template<typename string_type>
void int_to_string( string_type& target, std::size_t value )
{
- target = std::to_string(value);
+ // For ADL
+ using std::to_string;
+ target = to_string(value);
}
-template <typename IteratorType> class iteration_proxy_value
+template<typename IteratorType> class iteration_proxy_value
{
public:
using difference_type = std::ptrdiff_t;
@@ -3675,10 +4323,12 @@ template <typename IteratorType> class iteration_proxy_value
/// a string representation of the array index
mutable string_type array_index_str = "0";
/// an empty string (to return a reference for primitive values)
- const string_type empty_str = "";
+ const string_type empty_str{};
public:
- explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {}
+ explicit iteration_proxy_value(IteratorType it) noexcept
+ : anchor(std::move(it))
+ {}
/// dereference operator (needed for range-based for)
iteration_proxy_value& operator*()
@@ -3710,7 +4360,7 @@ template <typename IteratorType> class iteration_proxy_value
/// return key of the iterator
const string_type& key() const
{
- assert(anchor.m_object != nullptr);
+ JSON_ASSERT(anchor.m_object != nullptr);
switch (anchor.m_object->type())
{
@@ -3769,7 +4419,7 @@ template<typename IteratorType> class iteration_proxy
// 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>
+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();
@@ -3777,7 +4427,7 @@ auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decl
// 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>
+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();
@@ -3796,11 +4446,11 @@ namespace std
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif
-template <typename IteratorType>
+template<typename IteratorType>
class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>
: public std::integral_constant<std::size_t, 2> {};
-template <std::size_t N, typename IteratorType>
+template<std::size_t N, typename IteratorType>
class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
{
public:
@@ -3861,9 +4511,9 @@ struct external_constructor<value_t::string>
j.assert_invariant();
}
- template<typename BasicJsonType, typename CompatibleStringType,
- enable_if_t<not std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
- int> = 0>
+ 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_type = value_t::string;
@@ -3879,8 +4529,7 @@ struct external_constructor<value_t::binary>
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
{
j.m_type = value_t::binary;
- typename BasicJsonType::binary_t value{b};
- j.m_value = value;
+ j.m_value = typename BasicJsonType::binary_t(b);
j.assert_invariant();
}
@@ -3888,8 +4537,7 @@ struct external_constructor<value_t::binary>
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
{
j.m_type = value_t::binary;
- typename BasicJsonType::binary_t value{std::move(b)};
- j.m_value = value;
+ j.m_value = typename BasicJsonType::binary_t(std::move(b));;
j.assert_invariant();
}
};
@@ -3938,6 +4586,7 @@ struct external_constructor<value_t::array>
{
j.m_type = value_t::array;
j.m_value = arr;
+ j.set_parents();
j.assert_invariant();
}
@@ -3946,18 +4595,20 @@ struct external_constructor<value_t::array>
{
j.m_type = value_t::array;
j.m_value = std::move(arr);
+ j.set_parents();
j.assert_invariant();
}
- template<typename BasicJsonType, typename CompatibleArrayType,
- enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
- int> = 0>
+ 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_type = value_t::array;
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+ j.set_parents();
j.assert_invariant();
}
@@ -3970,6 +4621,7 @@ struct external_constructor<value_t::array>
for (const bool x : arr)
{
j.m_value.array->push_back(x);
+ j.set_parent(j.m_value.array->back());
}
j.assert_invariant();
}
@@ -3985,6 +4637,7 @@ struct external_constructor<value_t::array>
{
std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
}
+ j.set_parents();
j.assert_invariant();
}
};
@@ -3997,6 +4650,7 @@ struct external_constructor<value_t::object>
{
j.m_type = value_t::object;
j.m_value = obj;
+ j.set_parents();
j.assert_invariant();
}
@@ -4005,11 +4659,12 @@ struct external_constructor<value_t::object>
{
j.m_type = value_t::object;
j.m_value = std::move(obj);
+ j.set_parents();
j.assert_invariant();
}
- template<typename BasicJsonType, typename CompatibleObjectType,
- enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0>
+ 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;
@@ -4017,6 +4672,7 @@ struct external_constructor<value_t::object>
j.m_type = value_t::object;
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+ j.set_parents();
j.assert_invariant();
}
};
@@ -4080,20 +4736,20 @@ 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 and
- not is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value and
- not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and
- not std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value and
- not is_basic_json<CompatibleArrayType>::value,
- int> = 0>
+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 >
void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
{
external_constructor<value_t::array>::construct(j, arr);
}
-template <typename BasicJsonType>
+template<typename BasicJsonType>
void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
{
external_constructor<value_t::binary>::construct(j, bin);
@@ -4112,8 +4768,8 @@ 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 and not is_basic_json<CompatibleObjectType>::value, int> = 0>
+template < typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
{
external_constructor<value_t::object>::construct(j, obj);
@@ -4127,10 +4783,10 @@ void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
template <
typename BasicJsonType, typename T, std::size_t N,
- enable_if_t<not std::is_constructible<typename BasicJsonType::string_t,
- const T(&)[N]>::value,
- int> = 0 >
-void to_json(BasicJsonType& j, const T(&arr)[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 >
+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);
}
@@ -4142,8 +4798,8 @@ void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
}
// 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>
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
void to_json(BasicJsonType& j, const T& b)
{
j = { {b.key(), b.value()} };
@@ -4173,17 +4829,23 @@ struct to_json_fn
} // namespace detail
/// namespace to hold default `to_json` function
-namespace
+/// 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)
{
-constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
+constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; // NOLINT(misc-definitions-in-headers)
} // namespace
} // namespace nlohmann
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
namespace nlohmann
{
-template<typename, typename>
+template<typename ValueType, typename>
struct adl_serializer
{
/*!
@@ -4192,11 +4854,13 @@ struct adl_serializer
This function is usually called by the `get()` function of the
@ref basic_json class (either explicit or via conversion operators).
+ @note This function is chosen for default-constructible value types.
+
@param[in] j JSON value to read from
@param[in,out] val value to write to
*/
- template<typename BasicJsonType, typename ValueType>
- static auto from_json(BasicJsonType&& j, ValueType& val) noexcept(
+ 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())
{
@@ -4204,6 +4868,26 @@ struct adl_serializer
}
/*!
+ @brief convert a JSON value to any value type
+
+ This function is usually called by the `get()` function of the
+ @ref basic_json class (either explicit or via conversion operators).
+
+ @note This function is chosen for value types which are not default-constructible.
+
+ @param[in] j JSON value to read from
+
+ @return copy of the JSON value, converted to @a ValueType
+ */
+ 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
This function is usually called by the constructors of the @ref basic_json
@@ -4212,15 +4896,14 @@ struct adl_serializer
@param[in,out] j JSON value to write to
@param[in] val value to read from
*/
- template <typename BasicJsonType, typename ValueType>
- static auto to_json(BasicJsonType& j, ValueType&& val) noexcept(
- noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
- -> decltype(::nlohmann::to_json(j, std::forward<ValueType>(val)), void())
+ 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<ValueType>(val));
+ ::nlohmann::to_json(j, std::forward<TargetType>(val));
}
};
-
} // namespace nlohmann
// #include <nlohmann/byte_container_with_subtype.hpp>
@@ -4265,15 +4948,15 @@ class byte_container_with_subtype : public BinaryType
: container_type(std::move(b))
{}
- byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b)))
+ byte_container_with_subtype(const container_type& b, std::uint8_t subtype_) noexcept(noexcept(container_type(b)))
: container_type(b)
- , m_subtype(subtype)
+ , m_subtype(subtype_)
, m_has_subtype(true)
{}
- byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b))))
+ byte_container_with_subtype(container_type&& b, std::uint8_t subtype_) noexcept(noexcept(container_type(std::move(b))))
: container_type(std::move(b))
- , m_subtype(subtype)
+ , m_subtype(subtype_)
, m_has_subtype(true)
{}
@@ -4299,16 +4982,16 @@ class byte_container_with_subtype : public BinaryType
@exceptionsafety No-throw guarantee: this member function never throws
exceptions.
- @sa @ref subtype() -- return the binary subtype
- @sa @ref clear_subtype() -- clears the binary subtype
- @sa @ref has_subtype() -- returns whether or not the binary value has a
+ @sa see @ref subtype() -- return the binary subtype
+ @sa see @ref clear_subtype() -- clears the binary subtype
+ @sa see @ref has_subtype() -- returns whether or not the binary value has a
subtype
@since version 3.8.0
*/
- void set_subtype(std::uint8_t subtype) noexcept
+ void set_subtype(std::uint8_t subtype_) noexcept
{
- m_subtype = subtype;
+ m_subtype = subtype_;
m_has_subtype = true;
}
@@ -4326,9 +5009,9 @@ class byte_container_with_subtype : public BinaryType
@exceptionsafety No-throw guarantee: this member function never throws
exceptions.
- @sa @ref set_subtype() -- sets the binary subtype
- @sa @ref clear_subtype() -- clears the binary subtype
- @sa @ref has_subtype() -- returns whether or not the binary value has a
+ @sa see @ref set_subtype() -- sets the binary subtype
+ @sa see @ref clear_subtype() -- clears the binary subtype
+ @sa see @ref has_subtype() -- returns whether or not the binary value has a
subtype
@since version 3.8.0
@@ -4348,9 +5031,9 @@ class byte_container_with_subtype : public BinaryType
@exceptionsafety No-throw guarantee: this member function never throws
exceptions.
- @sa @ref subtype() -- return the binary subtype
- @sa @ref set_subtype() -- sets the binary subtype
- @sa @ref clear_subtype() -- clears the binary subtype
+ @sa see @ref subtype() -- return the binary subtype
+ @sa see @ref set_subtype() -- sets the binary subtype
+ @sa see @ref clear_subtype() -- clears the binary subtype
@since version 3.8.0
*/
@@ -4371,9 +5054,9 @@ class byte_container_with_subtype : public BinaryType
@exceptionsafety No-throw guarantee: this member function never throws
exceptions.
- @sa @ref subtype() -- return the binary subtype
- @sa @ref set_subtype() -- sets the binary subtype
- @sa @ref has_subtype() -- returns whether or not the binary value has a
+ @sa see @ref subtype() -- return the binary subtype
+ @sa see @ref set_subtype() -- sets the binary subtype
+ @sa see @ref has_subtype() -- returns whether or not the binary value has a
subtype
@since version 3.8.0
@@ -4391,20 +5074,141 @@ class byte_container_with_subtype : public BinaryType
} // namespace nlohmann
-// #include <nlohmann/detail/boolean_operators.hpp>
-
// #include <nlohmann/detail/conversions/from_json.hpp>
// #include <nlohmann/detail/conversions/to_json.hpp>
// #include <nlohmann/detail/exceptions.hpp>
+// #include <nlohmann/detail/hash.hpp>
+
+
+#include <cstdint> // uint8_t
+#include <cstddef> // size_t
+#include <functional> // hash
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+namespace nlohmann
+{
+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, 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
+} // namespace nlohmann
+
// #include <nlohmann/detail/input/binary_reader.hpp>
#include <algorithm> // generate_n
#include <array> // array
-#include <cassert> // assert
#include <cmath> // ldexp
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
@@ -4414,6 +5218,7 @@ class byte_container_with_subtype : public BinaryType
#include <limits> // numeric_limits
#include <string> // char_traits, string
#include <utility> // make_pair, move
+#include <vector> // vector
// #include <nlohmann/detail/exceptions.hpp>
@@ -4421,7 +5226,6 @@ class byte_container_with_subtype : public BinaryType
#include <array> // array
-#include <cassert> // assert
#include <cstddef> // size_t
#include <cstdio> //FILE *
#include <cstring> // strlen
@@ -4459,15 +5263,16 @@ class file_input_adapter
using char_type = char;
JSON_HEDLEY_NON_NULL(2)
- explicit file_input_adapter(std::FILE* f) noexcept
+ explicit file_input_adapter(std::FILE* f) noexcept
: m_file(f)
{}
// make class move-only
file_input_adapter(const file_input_adapter&) = delete;
- file_input_adapter(file_input_adapter&&) = default;
+ 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
{
@@ -4498,7 +5303,7 @@ class input_stream_adapter
{
// clear stream flags; we use underlying streambuf I/O, do not
// maintain ifstream flags, except eof
- if (is)
+ if (is != nullptr)
{
is->clear(is->rdstate() & std::ios::eofbit);
}
@@ -4511,9 +5316,10 @@ class input_stream_adapter
// 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&& rhs) = delete;
+ input_stream_adapter& operator=(input_stream_adapter&&) = delete;
- input_stream_adapter(input_stream_adapter&& rhs) : is(rhs.is), sb(rhs.sb)
+ input_stream_adapter(input_stream_adapter&& rhs) noexcept
+ : is(rhs.is), sb(rhs.sb)
{
rhs.is = nullptr;
rhs.sb = nullptr;
@@ -4526,7 +5332,7 @@ class input_stream_adapter
{
auto res = sb->sbumpc();
// set eof manually, as we don't use the istream interface.
- if (JSON_HEDLEY_UNLIKELY(res == EOF))
+ if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
{
is->clear(is->rdstate() | std::ios::eofbit);
}
@@ -4548,7 +5354,8 @@ class iterator_input_adapter
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)) {}
+ : current(std::move(first)), end(std::move(last))
+ {}
typename std::char_traits<char_type>::int_type get_character()
{
@@ -4558,10 +5365,8 @@ class iterator_input_adapter
std::advance(current, 1);
return result;
}
- else
- {
- return std::char_traits<char_type>::eof();
- }
+
+ return std::char_traits<char_type>::eof();
}
private:
@@ -4575,7 +5380,6 @@ class iterator_input_adapter
{
return current == end;
}
-
};
@@ -4673,7 +5477,7 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
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 or wc >= 0xE000)
+ 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));
@@ -4682,7 +5486,7 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
}
else
{
- if (JSON_HEDLEY_UNLIKELY(not input.empty()))
+ 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));
@@ -4719,13 +5523,13 @@ class wide_string_input_adapter
{
fill_buffer<sizeof(WideCharType)>();
- assert(utf8_bytes_filled > 0);
- assert(utf8_bytes_index == 0);
+ JSON_ASSERT(utf8_bytes_filled > 0);
+ JSON_ASSERT(utf8_bytes_index == 0);
}
// use buffer
- assert(utf8_bytes_filled > 0);
- assert(utf8_bytes_index < utf8_bytes_filled);
+ JSON_ASSERT(utf8_bytes_filled > 0);
+ JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
return utf8_bytes[utf8_bytes_index++];
}
@@ -4794,15 +5598,37 @@ typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapte
}
// Convenience shorthand from container to iterator
-template<typename ContainerType>
-auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container)))
+// 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
{
- // Enable ADL
- using std::begin;
- using std::end;
+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);
+}
// Special cases with fast paths
inline file_input_adapter input_adapter(std::FILE* file)
@@ -4825,20 +5651,20 @@ using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const
// Null-delimited strings, and the like.
template < typename CharT,
typename std::enable_if <
- std::is_pointer<CharT>::value and
- not std::is_array<CharT>::value and
- std::is_integral<typename std::remove_pointer<CharT>::type>::value and
+ 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));
- auto ptr = 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))
+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);
}
@@ -4849,12 +5675,12 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N))
class span_input_adapter
{
public:
- template<typename CharT,
- typename std::enable_if<
- std::is_pointer<CharT>::value and
- std::is_integral<typename std::remove_pointer<CharT>::type>::value and
- sizeof(typename std::remove_pointer<CharT>::type) == 1,
- int>::type = 0>
+ 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) {}
@@ -4867,7 +5693,7 @@ class span_input_adapter
contiguous_bytes_input_adapter&& get()
{
- return std::move(ia);
+ return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
}
private:
@@ -4879,7 +5705,6 @@ class span_input_adapter
// #include <nlohmann/detail/input/json_sax.hpp>
-#include <cassert> // assert
#include <cstddef>
#include <string> // string
#include <utility> // move
@@ -5008,6 +5833,11 @@ struct json_sax
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;
};
@@ -5038,7 +5868,7 @@ class json_sax_dom_parser
using binary_t = typename BasicJsonType::binary_t;
/*!
- @param[in, out] r reference to a JSON value that is manipulated while
+ @param[in,out] r reference to a JSON value that is manipulated while
parsing
@param[in] allow_exceptions_ whether parse errors yield exceptions
*/
@@ -5048,9 +5878,9 @@ class json_sax_dom_parser
// make class move-only
json_sax_dom_parser(const json_sax_dom_parser&) = delete;
- json_sax_dom_parser(json_sax_dom_parser&&) = default;
+ 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;
+ 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()
@@ -5099,10 +5929,9 @@ class json_sax_dom_parser
{
ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
- if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
+ if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
- JSON_THROW(out_of_range::create(408,
- "excessive object size: " + std::to_string(len)));
+ JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back()));
}
return true;
@@ -5117,6 +5946,7 @@ class json_sax_dom_parser
bool end_object()
{
+ ref_stack.back()->set_parents();
ref_stack.pop_back();
return true;
}
@@ -5125,10 +5955,9 @@ class json_sax_dom_parser
{
ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
- if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
+ if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
- JSON_THROW(out_of_range::create(408,
- "excessive array size: " + std::to_string(len)));
+ JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back()));
}
return true;
@@ -5136,34 +5965,20 @@ class json_sax_dom_parser
bool end_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 detail::exception& ex)
+ const Exception& ex)
{
errored = true;
+ static_cast<void>(ex);
if (allow_exceptions)
{
- // determine the proper exception type from the id
- switch ((ex.id / 100) % 100)
- {
- case 1:
- JSON_THROW(*static_cast<const detail::parse_error*>(&ex));
- case 4:
- JSON_THROW(*static_cast<const detail::out_of_range*>(&ex));
- // LCOV_EXCL_START
- case 2:
- JSON_THROW(*static_cast<const detail::invalid_iterator*>(&ex));
- case 3:
- JSON_THROW(*static_cast<const detail::type_error*>(&ex));
- case 5:
- JSON_THROW(*static_cast<const detail::other_error*>(&ex));
- default:
- assert(false);
- // LCOV_EXCL_STOP
- }
+ JSON_THROW(ex);
}
return false;
}
@@ -5190,7 +6005,7 @@ class json_sax_dom_parser
return &root;
}
- assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
+ JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
if (ref_stack.back()->is_array())
{
@@ -5198,8 +6013,8 @@ class json_sax_dom_parser
return &(ref_stack.back()->m_value.array->back());
}
- assert(ref_stack.back()->is_object());
- assert(object_element);
+ JSON_ASSERT(ref_stack.back()->is_object());
+ JSON_ASSERT(object_element);
*object_element = BasicJsonType(std::forward<Value>(v));
return object_element;
}
@@ -5238,9 +6053,9 @@ class json_sax_dom_callback_parser
// 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;
+ 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;
+ 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()
@@ -5295,9 +6110,9 @@ class json_sax_dom_callback_parser
ref_stack.push_back(val.second);
// check object limit
- if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
+ if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
- JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len)));
+ JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back()));
}
return true;
@@ -5312,7 +6127,7 @@ class json_sax_dom_callback_parser
key_keep_stack.push_back(keep);
// add discarded value at given key and store the reference for later
- if (keep and ref_stack.back())
+ if (keep && ref_stack.back())
{
object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
}
@@ -5322,18 +6137,25 @@ class json_sax_dom_callback_parser
bool end_object()
{
- if (ref_stack.back() and not callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
+ if (ref_stack.back())
{
- // discard object
- *ref_stack.back() = discarded;
+ 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();
+ }
}
- assert(not ref_stack.empty());
- assert(not keep_stack.empty());
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(!keep_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
- if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_structured())
+ 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)
@@ -5358,9 +6180,9 @@ class json_sax_dom_callback_parser
ref_stack.push_back(val.second);
// check array limit
- if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))
+ if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
- JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len)));
+ JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back()));
}
return true;
@@ -5373,20 +6195,24 @@ class json_sax_dom_callback_parser
if (ref_stack.back())
{
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
- if (not keep)
+ if (keep)
+ {
+ ref_stack.back()->set_parents();
+ }
+ else
{
// discard array
*ref_stack.back() = discarded;
}
}
- assert(not ref_stack.empty());
- assert(not keep_stack.empty());
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(!keep_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
// remove discarded value
- if (not keep and not ref_stack.empty() and ref_stack.back()->is_array())
+ if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->pop_back();
}
@@ -5394,30 +6220,15 @@ class json_sax_dom_callback_parser
return true;
}
+ template<class Exception>
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
- const detail::exception& ex)
+ const Exception& ex)
{
errored = true;
+ static_cast<void>(ex);
if (allow_exceptions)
{
- // determine the proper exception type from the id
- switch ((ex.id / 100) % 100)
- {
- case 1:
- JSON_THROW(*static_cast<const detail::parse_error*>(&ex));
- case 4:
- JSON_THROW(*static_cast<const detail::out_of_range*>(&ex));
- // LCOV_EXCL_START
- case 2:
- JSON_THROW(*static_cast<const detail::invalid_iterator*>(&ex));
- case 3:
- JSON_THROW(*static_cast<const detail::type_error*>(&ex));
- case 5:
- JSON_THROW(*static_cast<const detail::other_error*>(&ex));
- default:
- assert(false);
- // LCOV_EXCL_STOP
- }
+ JSON_THROW(ex);
}
return false;
}
@@ -5446,11 +6257,11 @@ class json_sax_dom_callback_parser
template<typename Value>
std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
{
- assert(not keep_stack.empty());
+ JSON_ASSERT(!keep_stack.empty());
// do not handle this value if we know it would be added to a discarded
// container
- if (not keep_stack.back())
+ if (!keep_stack.back())
{
return {false, nullptr};
}
@@ -5459,10 +6270,10 @@ class json_sax_dom_callback_parser
auto value = BasicJsonType(std::forward<Value>(v));
// check callback
- const bool keep = skip_callback or callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
+ 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 (not keep)
+ if (!keep)
{
return {false, nullptr};
}
@@ -5475,34 +6286,34 @@ class json_sax_dom_callback_parser
// skip this value if we already decided to skip the parent
// (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
- if (not ref_stack.back())
+ if (!ref_stack.back())
{
return {false, nullptr};
}
// we now only expect arrays and objects
- assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
+ JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
// array
if (ref_stack.back()->is_array())
{
- ref_stack.back()->m_value.array->push_back(std::move(value));
+ ref_stack.back()->m_value.array->emplace_back(std::move(value));
return {true, &(ref_stack.back()->m_value.array->back())};
}
// object
- assert(ref_stack.back()->is_object());
+ JSON_ASSERT(ref_stack.back()->is_object());
// check if we should store an element for the current key
- assert(not key_keep_stack.empty());
+ JSON_ASSERT(!key_keep_stack.empty());
const bool store_element = key_keep_stack.back();
key_keep_stack.pop_back();
- if (not store_element)
+ if (!store_element)
{
return {false, nullptr};
}
- assert(object_element);
+ JSON_ASSERT(object_element);
*object_element = std::move(value);
return {true, object_element};
}
@@ -5606,6 +6417,1634 @@ class json_sax_acceptor
} // namespace nlohmann
+// #include <nlohmann/detail/input/lexer.hpp>
+
+
+#include <array> // array
+#include <clocale> // localeconv
+#include <cstddef> // size_t
+#include <cstdio> // snprintf
+#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
+#include <initializer_list> // initializer_list
+#include <string> // char_traits, string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/position_t.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+namespace nlohmann
+{
+namespace detail
+{
+///////////
+// lexer //
+///////////
+
+template<typename BasicJsonType>
+class lexer_base
+{
+ public:
+ /// token types for the parser
+ enum class token_type
+ {
+ uninitialized, ///< indicating the scanner is uninitialized
+ literal_true, ///< the `true` literal
+ literal_false, ///< the `false` literal
+ literal_null, ///< the `null` literal
+ value_string, ///< a string -- use get_string() for actual value
+ value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value
+ value_integer, ///< a signed integer -- use get_number_integer() for actual value
+ value_float, ///< an floating point number -- use get_number_float() for actual value
+ begin_array, ///< the character for array begin `[`
+ begin_object, ///< the character for object begin `{`
+ end_array, ///< the character for array end `]`
+ end_object, ///< the character for object end `}`
+ name_separator, ///< the name separator `:`
+ value_separator, ///< the value separator `,`
+ parse_error, ///< indicating a parse error
+ end_of_input, ///< indicating the end of the input buffer
+ literal_or_value ///< a literal or the begin of a value (only for diagnostics)
+ };
+
+ /// return name of values of type token_type (only used for errors)
+ JSON_HEDLEY_RETURNS_NON_NULL
+ JSON_HEDLEY_CONST
+ static const char* token_type_name(const token_type t) noexcept
+ {
+ switch (t)
+ {
+ case token_type::uninitialized:
+ return "<uninitialized>";
+ case token_type::literal_true:
+ return "true literal";
+ case token_type::literal_false:
+ return "false literal";
+ case token_type::literal_null:
+ return "null literal";
+ case token_type::value_string:
+ return "string literal";
+ case token_type::value_unsigned:
+ case token_type::value_integer:
+ case token_type::value_float:
+ return "number literal";
+ case token_type::begin_array:
+ return "'['";
+ case token_type::begin_object:
+ return "'{'";
+ case token_type::end_array:
+ return "']'";
+ case token_type::end_object:
+ return "'}'";
+ case token_type::name_separator:
+ return "':'";
+ case token_type::value_separator:
+ return "','";
+ case token_type::parse_error:
+ return "<parse error>";
+ case token_type::end_of_input:
+ return "end of input";
+ case token_type::literal_or_value:
+ return "'[', '{', or a literal";
+ // LCOV_EXCL_START
+ default: // catch non-enum values
+ return "unknown token";
+ // LCOV_EXCL_STOP
+ }
+ }
+};
+/*!
+@brief lexical analysis
+
+This class organizes the lexical analysis during JSON deserialization.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class lexer : public lexer_base<BasicJsonType>
+{
+ 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 char_type = typename InputAdapterType::char_type;
+ using char_int_type = typename std::char_traits<char_type>::int_type;
+
+ public:
+ using token_type = typename lexer_base<BasicJsonType>::token_type;
+
+ explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept
+ : ia(std::move(adapter))
+ , ignore_comments(ignore_comments_)
+ , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
+ {}
+
+ // delete because of pointer members
+ lexer(const lexer&) = delete;
+ lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ lexer& operator=(lexer&) = delete;
+ lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ ~lexer() = default;
+
+ private:
+ /////////////////////
+ // locales
+ /////////////////////
+
+ /// return the locale-dependent decimal point
+ JSON_HEDLEY_PURE
+ static char get_decimal_point() noexcept
+ {
+ const auto* loc = localeconv();
+ JSON_ASSERT(loc != nullptr);
+ return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
+ }
+
+ /////////////////////
+ // scan functions
+ /////////////////////
+
+ /*!
+ @brief get codepoint from 4 hex characters following `\u`
+
+ For input "\u c1 c2 c3 c4" the codepoint is:
+ (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
+ = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
+
+ Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
+ must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
+ conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
+ between the ASCII value of the character and the desired integer value.
+
+ @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
+ non-hex character)
+ */
+ int get_codepoint()
+ {
+ // this function only makes sense after reading `\u`
+ JSON_ASSERT(current == 'u');
+ int codepoint = 0;
+
+ const auto factors = { 12u, 8u, 4u, 0u };
+ for (const auto factor : factors)
+ {
+ get();
+
+ if (current >= '0' && current <= '9')
+ {
+ codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);
+ }
+ else if (current >= 'A' && current <= 'F')
+ {
+ codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);
+ }
+ else if (current >= 'a' && current <= 'f')
+ {
+ codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);
+ return codepoint;
+ }
+
+ /*!
+ @brief check if the next byte(s) are inside a given range
+
+ Adds the current byte and, for each passed range, reads a new byte and
+ checks if it is inside the range. If a violation was detected, set up an
+ error message and return false. Otherwise, return true.
+
+ @param[in] ranges list of integers; interpreted as list of pairs of
+ inclusive lower and upper bound, respectively
+
+ @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
+ 1, 2, or 3 pairs. This precondition is enforced by an assertion.
+
+ @return true if and only if no range violation was detected
+ */
+ bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
+ {
+ JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
+ add(current);
+
+ for (auto range = ranges.begin(); range != ranges.end(); ++range)
+ {
+ get();
+ if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range)))
+ {
+ add(current);
+ }
+ else
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /*!
+ @brief scan a string literal
+
+ This function scans a string according to Sect. 7 of RFC 8259. While
+ scanning, bytes are escaped and copied into buffer token_buffer. Then the
+ function returns successfully, token_buffer is *not* null-terminated (as it
+ may contain \0 bytes), and token_buffer.size() is the number of bytes in the
+ string.
+
+ @return token_type::value_string if string could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note In case of errors, variable error_message contains a textual
+ description.
+ */
+ token_type scan_string()
+ {
+ // reset token_buffer (ignore opening quote)
+ reset();
+
+ // we entered the function by reading an open quote
+ JSON_ASSERT(current == '\"');
+
+ while (true)
+ {
+ // get next character
+ switch (get())
+ {
+ // end of file while parsing string
+ case std::char_traits<char_type>::eof():
+ {
+ error_message = "invalid string: missing closing quote";
+ return token_type::parse_error;
+ }
+
+ // closing quote
+ case '\"':
+ {
+ return token_type::value_string;
+ }
+
+ // escapes
+ case '\\':
+ {
+ switch (get())
+ {
+ // quotation mark
+ case '\"':
+ add('\"');
+ break;
+ // reverse solidus
+ case '\\':
+ add('\\');
+ break;
+ // solidus
+ case '/':
+ add('/');
+ break;
+ // backspace
+ case 'b':
+ add('\b');
+ break;
+ // form feed
+ case 'f':
+ add('\f');
+ break;
+ // line feed
+ case 'n':
+ add('\n');
+ break;
+ // carriage return
+ case 'r':
+ add('\r');
+ break;
+ // tab
+ case 't':
+ add('\t');
+ break;
+
+ // unicode escapes
+ case 'u':
+ {
+ const int codepoint1 = get_codepoint();
+ int codepoint = codepoint1; // start with codepoint1
+
+ if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if code point is a high surrogate
+ if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)
+ {
+ // expect next \uxxxx entry
+ if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u'))
+ {
+ const int codepoint2 = get_codepoint();
+
+ if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if codepoint2 is a low surrogate
+ if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))
+ {
+ // overwrite codepoint
+ codepoint = static_cast<int>(
+ // high surrogate occupies the most significant 22 bits
+ (static_cast<unsigned int>(codepoint1) << 10u)
+ // low surrogate occupies the least significant 15 bits
+ + static_cast<unsigned int>(codepoint2)
+ // there is still the 0xD800, 0xDC00 and 0x10000 noise
+ // in the result so we have to subtract with:
+ // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
+ - 0x35FDC00u);
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
+ return token_type::parse_error;
+ }
+ }
+
+ // result of the above calculation yields a proper codepoint
+ JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
+
+ // translate codepoint into bytes
+ if (codepoint < 0x80)
+ {
+ // 1-byte characters: 0xxxxxxx (ASCII)
+ add(static_cast<char_int_type>(codepoint));
+ }
+ else if (codepoint <= 0x7FF)
+ {
+ // 2-byte characters: 110xxxxx 10xxxxxx
+ add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));
+ add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+ }
+ else if (codepoint <= 0xFFFF)
+ {
+ // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+ add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));
+ add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+ add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+ }
+ else
+ {
+ // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));
+ add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));
+ add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+ add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+ }
+
+ break;
+ }
+
+ // other characters after escape
+ default:
+ error_message = "invalid string: forbidden character after backslash";
+ return token_type::parse_error;
+ }
+
+ break;
+ }
+
+ // invalid control characters
+ case 0x00:
+ {
+ error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
+ return token_type::parse_error;
+ }
+
+ case 0x01:
+ {
+ error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
+ return token_type::parse_error;
+ }
+
+ case 0x02:
+ {
+ error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
+ return token_type::parse_error;
+ }
+
+ case 0x03:
+ {
+ error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
+ return token_type::parse_error;
+ }
+
+ case 0x04:
+ {
+ error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
+ return token_type::parse_error;
+ }
+
+ case 0x05:
+ {
+ error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
+ return token_type::parse_error;
+ }
+
+ case 0x06:
+ {
+ error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
+ return token_type::parse_error;
+ }
+
+ case 0x07:
+ {
+ error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
+ return token_type::parse_error;
+ }
+
+ case 0x08:
+ {
+ error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
+ return token_type::parse_error;
+ }
+
+ case 0x09:
+ {
+ error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
+ return token_type::parse_error;
+ }
+
+ case 0x0A:
+ {
+ error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
+ return token_type::parse_error;
+ }
+
+ case 0x0B:
+ {
+ error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
+ return token_type::parse_error;
+ }
+
+ case 0x0C:
+ {
+ error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
+ return token_type::parse_error;
+ }
+
+ case 0x0D:
+ {
+ error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
+ return token_type::parse_error;
+ }
+
+ case 0x0E:
+ {
+ error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
+ return token_type::parse_error;
+ }
+
+ case 0x0F:
+ {
+ error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
+ return token_type::parse_error;
+ }
+
+ case 0x10:
+ {
+ error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
+ return token_type::parse_error;
+ }
+
+ case 0x11:
+ {
+ error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
+ return token_type::parse_error;
+ }
+
+ case 0x12:
+ {
+ error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
+ return token_type::parse_error;
+ }
+
+ case 0x13:
+ {
+ error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
+ return token_type::parse_error;
+ }
+
+ case 0x14:
+ {
+ error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
+ return token_type::parse_error;
+ }
+
+ case 0x15:
+ {
+ error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
+ return token_type::parse_error;
+ }
+
+ case 0x16:
+ {
+ error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
+ return token_type::parse_error;
+ }
+
+ case 0x17:
+ {
+ error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
+ return token_type::parse_error;
+ }
+
+ case 0x18:
+ {
+ error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
+ return token_type::parse_error;
+ }
+
+ case 0x19:
+ {
+ error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
+ return token_type::parse_error;
+ }
+
+ case 0x1A:
+ {
+ error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
+ return token_type::parse_error;
+ }
+
+ case 0x1B:
+ {
+ error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
+ return token_type::parse_error;
+ }
+
+ case 0x1C:
+ {
+ error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
+ return token_type::parse_error;
+ }
+
+ case 0x1D:
+ {
+ error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
+ return token_type::parse_error;
+ }
+
+ case 0x1E:
+ {
+ error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
+ return token_type::parse_error;
+ }
+
+ case 0x1F:
+ {
+ error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
+ return token_type::parse_error;
+ }
+
+ // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
+ case 0x20:
+ case 0x21:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ {
+ add(current);
+ break;
+ }
+
+ // U+0080..U+07FF: bytes C2..DF 80..BF
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xD0:
+ case 0xD1:
+ case 0xD2:
+ case 0xD3:
+ case 0xD4:
+ case 0xD5:
+ case 0xD6:
+ case 0xD7:
+ case 0xD8:
+ case 0xD9:
+ case 0xDA:
+ case 0xDB:
+ case 0xDC:
+ case 0xDD:
+ case 0xDE:
+ case 0xDF:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
+ case 0xE0:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
+ // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xEE:
+ case 0xEF:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+D000..U+D7FF: bytes ED 80..9F 80..BF
+ case 0xED:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
+ case 0xF0:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
+ case 0xF4:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // remaining bytes (80..C1 and F5..FF) are ill-formed
+ default:
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return token_type::parse_error;
+ }
+ }
+ }
+ }
+
+ /*!
+ * @brief scan a comment
+ * @return whether comment could be scanned successfully
+ */
+ bool scan_comment()
+ {
+ switch (get())
+ {
+ // single-line comments skip input until a newline or EOF is read
+ case '/':
+ {
+ while (true)
+ {
+ switch (get())
+ {
+ case '\n':
+ case '\r':
+ case std::char_traits<char_type>::eof():
+ case '\0':
+ return true;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // multi-line comments skip input until */ is read
+ case '*':
+ {
+ while (true)
+ {
+ switch (get())
+ {
+ case std::char_traits<char_type>::eof():
+ case '\0':
+ {
+ error_message = "invalid comment; missing closing '*/'";
+ return false;
+ }
+
+ case '*':
+ {
+ switch (get())
+ {
+ case '/':
+ return true;
+
+ default:
+ {
+ unget();
+ continue;
+ }
+ }
+ }
+
+ default:
+ continue;
+ }
+ }
+ }
+
+ // unexpected character after reading '/'
+ default:
+ {
+ error_message = "invalid comment; expecting '/' or '*' after '/'";
+ return false;
+ }
+ }
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ static void strtof(float& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtof(str, endptr);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ static void strtof(double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtod(str, endptr);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ static void strtof(long double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtold(str, endptr);
+ }
+
+ /*!
+ @brief scan a number literal
+
+ This function scans a string according to Sect. 6 of RFC 8259.
+
+ The function is realized with a deterministic finite state machine derived
+ from the grammar described in RFC 8259. Starting in state "init", the
+ input is read and used to determined the next state. Only state "done"
+ accepts the number. State "error" is a trap state to model errors. In the
+ table below, "anything" means any character but the ones listed before.
+
+ state | 0 | 1-9 | e E | + | - | . | anything
+ ---------|----------|----------|----------|---------|---------|----------|-----------
+ init | zero | any1 | [error] | [error] | minus | [error] | [error]
+ minus | zero | any1 | [error] | [error] | [error] | [error] | [error]
+ zero | done | done | exponent | done | done | decimal1 | done
+ any1 | any1 | any1 | exponent | done | done | decimal1 | done
+ decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error]
+ decimal2 | decimal2 | decimal2 | exponent | done | done | done | done
+ exponent | any2 | any2 | [error] | sign | sign | [error] | [error]
+ sign | any2 | any2 | [error] | [error] | [error] | [error] | [error]
+ any2 | any2 | any2 | done | done | done | done | done
+
+ The state machine is realized with one label per state (prefixed with
+ "scan_number_") and `goto` statements between them. The state machine
+ contains cycles, but any cycle can be left when EOF is read. Therefore,
+ the function is guaranteed to terminate.
+
+ During scanning, the read bytes are stored in token_buffer. This string is
+ then converted to a signed integer, an unsigned integer, or a
+ floating-point number.
+
+ @return token_type::value_unsigned, token_type::value_integer, or
+ token_type::value_float if number could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note The scanner is independent of the current locale. Internally, the
+ locale's decimal point is used instead of `.` to work with the
+ locale-dependent converters.
+ */
+ token_type scan_number() // lgtm [cpp/use-of-goto]
+ {
+ // reset token_buffer to store the number's bytes
+ reset();
+
+ // the type of the parsed number; initially set to unsigned; will be
+ // changed if minus sign, decimal point or exponent is read
+ token_type number_type = token_type::value_unsigned;
+
+ // state (init): we just found out we need to scan a number
+ switch (current)
+ {
+ case '-':
+ {
+ add(current);
+ goto scan_number_minus;
+ }
+
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ // all other characters are rejected outside scan_number()
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+
+scan_number_minus:
+ // state: we just parsed a leading minus sign
+ number_type = token_type::value_integer;
+ switch (get())
+ {
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '-'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_zero:
+ // state: we just parse a zero (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_any1:
+ // state: we just parsed a number 0-9 (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_decimal1:
+ // state: we just parsed a decimal point
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '.'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_decimal2:
+ // we just parsed at least one number after a decimal point
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_exponent:
+ // we just parsed an exponent
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '+':
+ case '-':
+ {
+ add(current);
+ goto scan_number_sign;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message =
+ "invalid number; expected '+', '-', or digit after exponent";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_sign:
+ // we just parsed an exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after exponent sign";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_any2:
+ // we just parsed a number after the exponent or exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_done:
+ // unget the character after the number (we only read it to know that
+ // we are done scanning a number)
+ unget();
+
+ char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ errno = 0;
+
+ // try to parse integers first and fall back to floats
+ if (number_type == token_type::value_unsigned)
+ {
+ const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
+
+ // we checked the number format before
+ JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+ if (errno == 0)
+ {
+ value_unsigned = static_cast<number_unsigned_t>(x);
+ if (value_unsigned == x)
+ {
+ return token_type::value_unsigned;
+ }
+ }
+ }
+ else if (number_type == token_type::value_integer)
+ {
+ const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
+
+ // we checked the number format before
+ JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+ if (errno == 0)
+ {
+ value_integer = static_cast<number_integer_t>(x);
+ if (value_integer == x)
+ {
+ return token_type::value_integer;
+ }
+ }
+ }
+
+ // this code is reached if we parse a floating-point number or if an
+ // integer conversion above failed
+ strtof(value_float, token_buffer.data(), &endptr);
+
+ // we checked the number format before
+ JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+ return token_type::value_float;
+ }
+
+ /*!
+ @param[in] literal_text the literal text to expect
+ @param[in] length the length of the passed literal text
+ @param[in] return_type the token type to return on success
+ */
+ JSON_HEDLEY_NON_NULL(2)
+ token_type scan_literal(const char_type* literal_text, const std::size_t length,
+ token_type return_type)
+ {
+ JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
+ for (std::size_t i = 1; i < length; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))
+ {
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+ return return_type;
+ }
+
+ /////////////////////
+ // input management
+ /////////////////////
+
+ /// reset token_buffer; current character is beginning of token
+ void reset() noexcept
+ {
+ token_buffer.clear();
+ token_string.clear();
+ token_string.push_back(std::char_traits<char_type>::to_char_type(current));
+ }
+
+ /*
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a
+ `std::char_traits<char>::eof()` in that case. Stores the scanned characters
+ for use in error messages.
+
+ @return character read from the input
+ */
+ char_int_type get()
+ {
+ ++position.chars_read_total;
+ ++position.chars_read_current_line;
+
+ if (next_unget)
+ {
+ // just reset the next_unget variable and work with current
+ next_unget = false;
+ }
+ else
+ {
+ current = ia.get_character();
+ }
+
+ if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+ {
+ token_string.push_back(std::char_traits<char_type>::to_char_type(current));
+ }
+
+ if (current == '\n')
+ {
+ ++position.lines_read;
+ position.chars_read_current_line = 0;
+ }
+
+ return current;
+ }
+
+ /*!
+ @brief unget current character (read it again on next get)
+
+ We implement unget by setting variable next_unget to true. The input is not
+ changed - we just simulate ungetting by modifying chars_read_total,
+ chars_read_current_line, and token_string. The next call to get() will
+ behave as if the unget character is read again.
+ */
+ void unget()
+ {
+ next_unget = true;
+
+ --position.chars_read_total;
+
+ // in case we "unget" a newline, we have to also decrement the lines_read
+ if (position.chars_read_current_line == 0)
+ {
+ if (position.lines_read > 0)
+ {
+ --position.lines_read;
+ }
+ }
+ else
+ {
+ --position.chars_read_current_line;
+ }
+
+ if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+ {
+ JSON_ASSERT(!token_string.empty());
+ token_string.pop_back();
+ }
+ }
+
+ /// add a character to token_buffer
+ void add(char_int_type c)
+ {
+ token_buffer.push_back(static_cast<typename string_t::value_type>(c));
+ }
+
+ public:
+ /////////////////////
+ // value getters
+ /////////////////////
+
+ /// return integer value
+ constexpr number_integer_t get_number_integer() const noexcept
+ {
+ return value_integer;
+ }
+
+ /// return unsigned integer value
+ constexpr number_unsigned_t get_number_unsigned() const noexcept
+ {
+ return value_unsigned;
+ }
+
+ /// return floating-point value
+ constexpr number_float_t get_number_float() const noexcept
+ {
+ return value_float;
+ }
+
+ /// return current string value (implicitly resets the token; useful only once)
+ string_t& get_string()
+ {
+ return token_buffer;
+ }
+
+ /////////////////////
+ // diagnostics
+ /////////////////////
+
+ /// return position of last read token
+ constexpr position_t get_position() const noexcept
+ {
+ return position;
+ }
+
+ /// return the last read token (for errors only). Will never contain EOF
+ /// (an arbitrary value that is not a valid char value, often -1), because
+ /// 255 may legitimately occur. May contain NUL, which should be escaped.
+ std::string get_token_string() const
+ {
+ // escape control characters
+ std::string result;
+ for (const auto c : token_string)
+ {
+ if (static_cast<unsigned char>(c) <= '\x1F')
+ {
+ // escape control characters
+ std::array<char, 9> cs{{}};
+ (std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ result += cs.data();
+ }
+ else
+ {
+ // add character as is
+ result.push_back(static_cast<std::string::value_type>(c));
+ }
+ }
+
+ return result;
+ }
+
+ /// return syntax error message
+ JSON_HEDLEY_RETURNS_NON_NULL
+ constexpr const char* get_error_message() const noexcept
+ {
+ return error_message;
+ }
+
+ /////////////////////
+ // actual scanner
+ /////////////////////
+
+ /*!
+ @brief skip the UTF-8 byte order mark
+ @return true iff there is no BOM or the correct BOM has been skipped
+ */
+ bool skip_bom()
+ {
+ if (get() == 0xEF)
+ {
+ // check if we completely parse the BOM
+ return get() == 0xBB && get() == 0xBF;
+ }
+
+ // the first character is not the beginning of the BOM; unget it to
+ // process is later
+ unget();
+ return true;
+ }
+
+ void skip_whitespace()
+ {
+ do
+ {
+ get();
+ }
+ while (current == ' ' || current == '\t' || current == '\n' || current == '\r');
+ }
+
+ token_type scan()
+ {
+ // initially, skip the BOM
+ if (position.chars_read_total == 0 && !skip_bom())
+ {
+ error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given";
+ return token_type::parse_error;
+ }
+
+ // read next character and ignore whitespace
+ skip_whitespace();
+
+ // ignore comments
+ while (ignore_comments && current == '/')
+ {
+ if (!scan_comment())
+ {
+ return token_type::parse_error;
+ }
+
+ // skip following whitespace
+ skip_whitespace();
+ }
+
+ switch (current)
+ {
+ // structural characters
+ case '[':
+ return token_type::begin_array;
+ case ']':
+ return token_type::end_array;
+ case '{':
+ return token_type::begin_object;
+ case '}':
+ return token_type::end_object;
+ case ':':
+ return token_type::name_separator;
+ case ',':
+ return token_type::value_separator;
+
+ // literals
+ case 't':
+ {
+ std::array<char_type, 4> true_literal = {{char_type('t'), char_type('r'), char_type('u'), char_type('e')}};
+ return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);
+ }
+ case 'f':
+ {
+ std::array<char_type, 5> false_literal = {{char_type('f'), char_type('a'), char_type('l'), char_type('s'), char_type('e')}};
+ return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);
+ }
+ case 'n':
+ {
+ std::array<char_type, 4> null_literal = {{char_type('n'), char_type('u'), char_type('l'), char_type('l')}};
+ return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);
+ }
+
+ // string
+ case '\"':
+ return scan_string();
+
+ // number
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return scan_number();
+
+ // end of input (the null byte is needed when parsing from
+ // string literals)
+ case '\0':
+ case std::char_traits<char_type>::eof():
+ return token_type::end_of_input;
+
+ // error
+ default:
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+
+ private:
+ /// input adapter
+ InputAdapterType ia;
+
+ /// whether comments should be ignored (true) or signaled as errors (false)
+ const bool ignore_comments = false;
+
+ /// the current character
+ char_int_type current = std::char_traits<char_type>::eof();
+
+ /// whether the next get() call should just return current
+ bool next_unget = false;
+
+ /// the start position of the current token
+ position_t position {};
+
+ /// raw input token string (for error messages)
+ std::vector<char_type> token_string {};
+
+ /// buffer for variable-length tokens (numbers, strings)
+ string_t token_buffer {};
+
+ /// a description of occurred lexer errors
+ const char* error_message = "";
+
+ // number values
+ number_integer_t value_integer = 0;
+ number_unsigned_t value_unsigned = 0;
+ number_float_t value_float = 0;
+
+ /// the decimal point
+ const char_int_type decimal_point_char = '.';
+};
+} // namespace detail
+} // namespace nlohmann
+
// #include <nlohmann/detail/macro_scope.hpp>
// #include <nlohmann/detail/meta/is_sax.hpp>
@@ -5624,53 +8063,57 @@ namespace nlohmann
{
namespace detail
{
-template <typename T>
+template<typename T>
using null_function_t = decltype(std::declval<T&>().null());
-template <typename T>
+template<typename T>
using boolean_function_t =
decltype(std::declval<T&>().boolean(std::declval<bool>()));
-template <typename T, typename Integer>
+template<typename T, typename Integer>
using number_integer_function_t =
decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
-template <typename T, typename Unsigned>
+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>
+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>
+template<typename T, typename String>
using string_function_t =
decltype(std::declval<T&>().string(std::declval<String&>()));
-template <typename T>
+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>
+template<typename T, typename String>
using key_function_t =
decltype(std::declval<T&>().key(std::declval<String&>()));
-template <typename T>
+template<typename T>
using end_object_function_t = decltype(std::declval<T&>().end_object());
-template <typename T>
+template<typename T>
using start_array_function_t =
decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
-template <typename T>
+template<typename T>
using end_array_function_t = decltype(std::declval<T&>().end_array());
-template <typename T, typename Exception>
+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>
+template<typename SAX, typename BasicJsonType>
struct is_sax
{
private:
@@ -5681,19 +8124,18 @@ struct is_sax
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, 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 &&
@@ -5702,7 +8144,7 @@ struct is_sax
is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
};
-template <typename SAX, typename BasicJsonType>
+template<typename SAX, typename BasicJsonType>
struct is_sax_static_asserts
{
private:
@@ -5713,6 +8155,7 @@ struct is_sax_static_asserts
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:
@@ -5736,6 +8179,9 @@ struct is_sax_static_asserts
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,
@@ -5762,6 +8208,13 @@ namespace nlohmann
namespace detail
{
+/// how to treat CBOR tags
+enum class cbor_tag_handler_t
+{
+ error, ///< throw a parse_error exception in case of a tag
+ ignore ///< ignore tags
+};
+
/*!
@brief determine system byte order
@@ -5800,29 +8253,31 @@ class binary_reader
@param[in] adapter input adapter to read from
*/
- explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter))
+ explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter))
{
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
}
// make class move-only
binary_reader(const binary_reader&) = delete;
- binary_reader(binary_reader&&) = default;
+ binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
binary_reader& operator=(const binary_reader&) = delete;
- binary_reader& operator=(binary_reader&&) = default;
+ binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
~binary_reader() = default;
/*!
@param[in] format the binary format to parse
@param[in] sax_ a SAX event processor
@param[in] strict whether to expect the input to be consumed completed
+ @param[in] tag_handler how to treat CBOR tags
- @return
+ @return whether parsing was successful
*/
JSON_HEDLEY_NON_NULL(3)
bool sax_parse(const input_format_t format,
json_sax_t* sax_,
- const bool strict = true)
+ const bool strict = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
sax = sax_;
bool result = false;
@@ -5834,7 +8289,7 @@ class binary_reader
break;
case input_format_t::cbor:
- result = parse_cbor_internal();
+ result = parse_cbor_internal(true, tag_handler);
break;
case input_format_t::msgpack:
@@ -5846,11 +8301,11 @@ class binary_reader
break;
default: // LCOV_EXCL_LINE
- assert(false); // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
// strict mode: next byte must be EOF
- if (result and strict)
+ if (result && strict)
{
if (format == input_format_t::ubjson)
{
@@ -5864,7 +8319,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))
{
return sax->parse_error(chars_read, get_token_string(),
- parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value")));
+ parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType()));
}
}
@@ -5882,15 +8337,15 @@ class binary_reader
*/
bool parse_bson_internal()
{
- std::int32_t document_size;
+ std::int32_t document_size{};
get_number<std::int32_t, true>(input_format_t::bson, document_size);
- if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1))))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))
{
return false;
}
- if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/false)))
+ if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))
{
return false;
}
@@ -5900,7 +8355,7 @@ class binary_reader
/*!
@brief Parses a C-style string from the BSON input.
- @param[in, out] result A reference to the string variable where the read
+ @param[in,out] result A reference to the string variable where the read
string is to be stored.
@return `true` if the \x00-byte indicating the end of the string was
encountered before the EOF; false` indicates an unexpected EOF.
@@ -5911,7 +8366,7 @@ class binary_reader
while (true)
{
get();
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "cstring")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring")))
{
return false;
}
@@ -5921,8 +8376,6 @@ class binary_reader
}
*out++ = static_cast<typename string_t::value_type>(current);
}
-
- return true;
}
/*!
@@ -5930,7 +8383,7 @@ class binary_reader
input.
@param[in] len The length (including the zero-byte at the end) of the
string to be read.
- @param[in, out] result A reference to the string variable where the read
+ @param[in,out] result A reference to the string variable where the read
string is to be stored.
@tparam NumberType The type of the length @a len
@pre len >= 1
@@ -5942,16 +8395,16 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(len < 1))
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType()));
}
- return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) and get() != std::char_traits<char_type>::eof();
+ return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();
}
/*!
@brief Parses a byte array input of length @a len from the BSON input.
@param[in] len The length of the byte array to be read.
- @param[in, out] result A reference to the binary variable where the read
+ @param[in,out] result A reference to the binary variable where the read
array is to be stored.
@tparam NumberType The type of the length @a len
@pre len >= 0
@@ -5963,11 +8416,11 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(len < 0))
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType()));
}
// All BSON binary values have a subtype
- std::uint8_t subtype;
+ std::uint8_t subtype{};
get_number<std::uint8_t>(input_format_t::bson, subtype);
result.set_subtype(subtype);
@@ -5991,15 +8444,15 @@ class binary_reader
{
case 0x01: // double
{
- double number;
- return get_number<double, true>(input_format_t::bson, number) and sax->number_float(static_cast<number_float_t>(number), "");
+ double number{};
+ return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0x02: // string
{
- std::int32_t len;
+ std::int32_t len{};
string_t value;
- return get_number<std::int32_t, true>(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value);
+ return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);
}
case 0x03: // object
@@ -6014,9 +8467,9 @@ class binary_reader
case 0x05: // binary
{
- std::int32_t len;
+ std::int32_t len{};
binary_t value;
- return get_number<std::int32_t, true>(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value);
+ return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);
}
case 0x08: // boolean
@@ -6031,21 +8484,21 @@ class binary_reader
case 0x10: // int32
{
- std::int32_t value;
- return get_number<std::int32_t, true>(input_format_t::bson, value) and sax->number_integer(value);
+ std::int32_t value{};
+ return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
}
case 0x12: // int64
{
- std::int64_t value;
- return get_number<std::int64_t, true>(input_format_t::bson, value) and sax->number_integer(value);
+ std::int64_t value{};
+ return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
}
default: // anything else not supported (yet)
{
std::array<char, 3> cr{{}};
- (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type));
- return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data())));
+ (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType()));
}
}
}
@@ -6068,23 +8521,23 @@ class binary_reader
while (auto element_type = get())
{
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list")))
{
return false;
}
const std::size_t element_type_parse_position = chars_read;
- if (JSON_HEDLEY_UNLIKELY(not get_bson_cstr(key)))
+ if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
{
return false;
}
- if (not is_array and not sax->key(key))
+ if (!is_array && !sax->key(key))
{
return false;
}
- if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_internal(element_type, element_type_parse_position)))
+ if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))
{
return false;
}
@@ -6102,15 +8555,15 @@ class binary_reader
*/
bool parse_bson_array()
{
- std::int32_t document_size;
+ std::int32_t document_size{};
get_number<std::int32_t, true>(input_format_t::bson, document_size);
- if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1))))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))
{
return false;
}
- if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/true)))
+ if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))
{
return false;
}
@@ -6124,12 +8577,14 @@ class binary_reader
/*!
@param[in] get_char whether a new character should be retrieved from the
- input (true, default) or whether the last read
- character should be considered instead
+ input (true) or whether the last read character should
+ be considered instead (false)
+ @param[in] tag_handler how CBOR tags should be treated
@return whether a valid CBOR value was passed to the SAX parser
*/
- bool parse_cbor_internal(const bool get_char = true)
+ bool parse_cbor_internal(const bool get_char,
+ const cbor_tag_handler_t tag_handler)
{
switch (get_char ? get() : current)
{
@@ -6166,26 +8621,26 @@ class binary_reader
case 0x18: // Unsigned integer (one-byte uint8_t follows)
{
- std::uint8_t number;
- return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);
+ std::uint8_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
case 0x19: // Unsigned integer (two-byte uint16_t follows)
{
- std::uint16_t number;
- return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);
+ std::uint16_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
case 0x1A: // Unsigned integer (four-byte uint32_t follows)
{
- std::uint32_t number;
- return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);
+ std::uint32_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
{
- std::uint64_t number;
- return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);
+ std::uint64_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
}
// Negative integer -1-0x00..-1-0x17 (-1..-24)
@@ -6217,26 +8672,26 @@ class binary_reader
case 0x38: // Negative integer (one-byte uint8_t follows)
{
- std::uint8_t number;
- return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);
+ std::uint8_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
}
case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
{
- std::uint16_t number;
- return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);
+ std::uint16_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
}
case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
{
- std::uint32_t number;
- return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);
+ std::uint32_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
}
case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
{
- std::uint64_t number;
- return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1)
+ std::uint64_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)
- static_cast<number_integer_t>(number));
}
@@ -6272,7 +8727,7 @@ class binary_reader
case 0x5F: // Binary data (indefinite length)
{
binary_t b;
- return get_cbor_binary(b) and sax->binary(b);
+ return get_cbor_binary(b) && sax->binary(b);
}
// UTF-8 string (0x00..0x17 bytes follow)
@@ -6307,7 +8762,7 @@ class binary_reader
case 0x7F: // UTF-8 string (indefinite length)
{
string_t s;
- return get_cbor_string(s) and sax->string(s);
+ return get_cbor_string(s) && sax->string(s);
}
// array (0x00..0x17 data items follow)
@@ -6335,34 +8790,34 @@ class binary_reader
case 0x95:
case 0x96:
case 0x97:
- return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
+ return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
case 0x98: // array (one-byte uint8_t for n follows)
{
- std::uint8_t len;
- return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}
case 0x99: // array (two-byte uint16_t for n follow)
{
- std::uint16_t len;
- return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}
case 0x9A: // array (four-byte uint32_t for n follow)
{
- std::uint32_t len;
- return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}
case 0x9B: // array (eight-byte uint64_t for n follow)
{
- std::uint64_t len;
- return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}
case 0x9F: // array (indefinite length)
- return get_cbor_array(std::size_t(-1));
+ return get_cbor_array(std::size_t(-1), tag_handler);
// map (0x00..0x17 pairs of data items follow)
case 0xA0:
@@ -6389,34 +8844,102 @@ class binary_reader
case 0xB5:
case 0xB6:
case 0xB7:
- return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
+ return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
case 0xB8: // map (one-byte uint8_t for n follows)
{
- std::uint8_t len;
- return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}
case 0xB9: // map (two-byte uint16_t for n follow)
{
- std::uint16_t len;
- return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}
case 0xBA: // map (four-byte uint32_t for n follow)
{
- std::uint32_t len;
- return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}
case 0xBB: // map (eight-byte uint64_t for n follow)
{
- std::uint64_t len;
- return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}
case 0xBF: // map (indefinite length)
- return get_cbor_object(std::size_t(-1));
+ return get_cbor_object(std::size_t(-1), tag_handler);
+
+ case 0xC6: // tagged item
+ case 0xC7:
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xD0:
+ case 0xD1:
+ case 0xD2:
+ case 0xD3:
+ case 0xD4:
+ case 0xD8: // tagged item (1 bytes follow)
+ case 0xD9: // tagged item (2 bytes follow)
+ case 0xDA: // tagged item (4 bytes follow)
+ case 0xDB: // tagged item (8 bytes follow)
+ {
+ switch (tag_handler)
+ {
+ case cbor_tag_handler_t::error:
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
+ }
+
+ case cbor_tag_handler_t::ignore:
+ {
+ switch (current)
+ {
+ case 0xD8:
+ {
+ std::uint8_t len{};
+ get_number(input_format_t::cbor, len);
+ break;
+ }
+ case 0xD9:
+ {
+ std::uint16_t len{};
+ get_number(input_format_t::cbor, len);
+ break;
+ }
+ case 0xDA:
+ {
+ std::uint32_t len{};
+ get_number(input_format_t::cbor, len);
+ break;
+ }
+ case 0xDB:
+ {
+ std::uint64_t len{};
+ get_number(input_format_t::cbor, len);
+ break;
+ }
+ default:
+ break;
+ }
+ return parse_cbor_internal(true, tag_handler);
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ return false; // LCOV_EXCL_LINE
+ }
+ }
case 0xF4: // false
return sax->boolean(false);
@@ -6430,12 +8953,12 @@ class binary_reader
case 0xF9: // Half-Precision Float (two-byte IEEE 754)
{
const auto byte1_raw = get();
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
{
return false;
}
const auto byte2_raw = get();
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
{
return false;
}
@@ -6456,8 +8979,8 @@ class binary_reader
{
const int exp = (half >> 10u) & 0x1Fu;
const unsigned int mant = half & 0x3FFu;
- assert(0 <= exp and exp <= 32);
- assert(mant <= 1024);
+ JSON_ASSERT(0 <= exp&& exp <= 32);
+ JSON_ASSERT(mant <= 1024);
switch (exp)
{
case 0:
@@ -6477,20 +9000,20 @@ class binary_reader
case 0xFA: // Single-Precision Float (four-byte IEEE 754)
{
- float number;
- return get_number(input_format_t::cbor, number) and sax->number_float(static_cast<number_float_t>(number), "");
+ float number{};
+ return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
{
- double number;
- return get_number(input_format_t::cbor, number) and sax->number_float(static_cast<number_float_t>(number), "");
+ double number{};
+ return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
default: // anything else (0xFF is handled inside the other types)
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
}
}
}
@@ -6508,7 +9031,7 @@ class binary_reader
*/
bool get_cbor_string(string_t& result)
{
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "string")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
{
return false;
}
@@ -6546,26 +9069,26 @@ class binary_reader
case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
{
- std::uint8_t len;
- return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
}
case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
{
- std::uint16_t len;
- return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
}
case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
{
- std::uint32_t len;
- return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
}
case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
{
- std::uint64_t len;
- return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
}
case 0x7F: // UTF-8 string (indefinite length)
@@ -6573,7 +9096,7 @@ class binary_reader
while (get() != 0xFF)
{
string_t chunk;
- if (not get_cbor_string(chunk))
+ if (!get_cbor_string(chunk))
{
return false;
}
@@ -6585,7 +9108,7 @@ class binary_reader
default:
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType()));
}
}
}
@@ -6603,7 +9126,7 @@ class binary_reader
*/
bool get_cbor_binary(binary_t& result)
{
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "binary")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
{
return false;
}
@@ -6641,29 +9164,29 @@ class binary_reader
case 0x58: // Binary data (one-byte uint8_t for n follows)
{
- std::uint8_t len;
- return get_number(input_format_t::cbor, len) and
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) &&
get_binary(input_format_t::cbor, len, result);
}
case 0x59: // Binary data (two-byte uint16_t for n follow)
{
- std::uint16_t len;
- return get_number(input_format_t::cbor, len) and
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) &&
get_binary(input_format_t::cbor, len, result);
}
case 0x5A: // Binary data (four-byte uint32_t for n follow)
{
- std::uint32_t len;
- return get_number(input_format_t::cbor, len) and
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) &&
get_binary(input_format_t::cbor, len, result);
}
case 0x5B: // Binary data (eight-byte uint64_t for n follow)
{
- std::uint64_t len;
- return get_number(input_format_t::cbor, len) and
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) &&
get_binary(input_format_t::cbor, len, result);
}
@@ -6672,7 +9195,7 @@ class binary_reader
while (get() != 0xFF)
{
binary_t chunk;
- if (not get_cbor_binary(chunk))
+ if (!get_cbor_binary(chunk))
{
return false;
}
@@ -6684,7 +9207,7 @@ class binary_reader
default:
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType()));
}
}
}
@@ -6692,11 +9215,13 @@ class binary_reader
/*!
@param[in] len the length of the array or std::size_t(-1) for an
array of indefinite size
+ @param[in] tag_handler how CBOR tags should be treated
@return whether array creation completed
*/
- bool get_cbor_array(const std::size_t len)
+ bool get_cbor_array(const std::size_t len,
+ const cbor_tag_handler_t tag_handler)
{
- if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len)))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
{
return false;
}
@@ -6705,7 +9230,7 @@ class binary_reader
{
for (std::size_t i = 0; i < len; ++i)
{
- if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal()))
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{
return false;
}
@@ -6715,7 +9240,7 @@ class binary_reader
{
while (get() != 0xFF)
{
- if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal(false)))
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
{
return false;
}
@@ -6728,11 +9253,13 @@ class binary_reader
/*!
@param[in] len the length of the object or std::size_t(-1) for an
object of indefinite size
+ @param[in] tag_handler how CBOR tags should be treated
@return whether object creation completed
*/
- bool get_cbor_object(const std::size_t len)
+ bool get_cbor_object(const std::size_t len,
+ const cbor_tag_handler_t tag_handler)
{
- if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len)))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
{
return false;
}
@@ -6743,12 +9270,12 @@ class binary_reader
for (std::size_t i = 0; i < len; ++i)
{
get();
- if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key)))
+ if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
{
return false;
}
- if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal()))
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{
return false;
}
@@ -6759,12 +9286,12 @@ class binary_reader
{
while (get() != 0xFF)
{
- if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key)))
+ if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
{
return false;
}
- if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal()))
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{
return false;
}
@@ -6997,7 +9524,7 @@ class binary_reader
case 0xDB: // str 32
{
string_t s;
- return get_msgpack_string(s) and sax->string(s);
+ return get_msgpack_string(s) && sax->string(s);
}
case 0xC0: // nil
@@ -7022,91 +9549,91 @@ class binary_reader
case 0xD8: // fixext 16
{
binary_t b;
- return get_msgpack_binary(b) and sax->binary(b);
+ return get_msgpack_binary(b) && sax->binary(b);
}
case 0xCA: // float 32
{
- float number;
- return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), "");
+ float number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xCB: // float 64
{
- double number;
- return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), "");
+ double number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 0xCC: // uint 8
{
- std::uint8_t number;
- return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);
+ std::uint8_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xCD: // uint 16
{
- std::uint16_t number;
- return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);
+ std::uint16_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xCE: // uint 32
{
- std::uint32_t number;
- return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);
+ std::uint32_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xCF: // uint 64
{
- std::uint64_t number;
- return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);
+ std::uint64_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
}
case 0xD0: // int 8
{
- std::int8_t number;
- return get_number(input_format_t::msgpack, number) and sax->number_integer(number);
+ std::int8_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xD1: // int 16
{
- std::int16_t number;
- return get_number(input_format_t::msgpack, number) and sax->number_integer(number);
+ std::int16_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xD2: // int 32
{
- std::int32_t number;
- return get_number(input_format_t::msgpack, number) and sax->number_integer(number);
+ std::int32_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xD3: // int 64
{
- std::int64_t number;
- return get_number(input_format_t::msgpack, number) and sax->number_integer(number);
+ std::int64_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
}
case 0xDC: // array 16
{
- std::uint16_t len;
- return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len));
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));
}
case 0xDD: // array 32
{
- std::uint32_t len;
- return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len));
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));
}
case 0xDE: // map 16
{
- std::uint16_t len;
- return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len));
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));
}
case 0xDF: // map 32
{
- std::uint32_t len;
- return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len));
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));
}
// negative fixint
@@ -7147,7 +9674,7 @@ class binary_reader
default: // anything else
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
}
}
}
@@ -7164,7 +9691,7 @@ class binary_reader
*/
bool get_msgpack_string(string_t& result)
{
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "string")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string")))
{
return false;
}
@@ -7210,26 +9737,26 @@ class binary_reader
case 0xD9: // str 8
{
- std::uint8_t len;
- return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);
+ std::uint8_t len{};
+ return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
}
case 0xDA: // str 16
{
- std::uint16_t len;
- return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
}
case 0xDB: // str 32
{
- std::uint32_t len;
- return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
}
default:
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType()));
}
}
}
@@ -7257,92 +9784,92 @@ class binary_reader
{
case 0xC4: // bin 8
{
- std::uint8_t len;
- return get_number(input_format_t::msgpack, len) and
+ std::uint8_t len{};
+ return get_number(input_format_t::msgpack, len) &&
get_binary(input_format_t::msgpack, len, result);
}
case 0xC5: // bin 16
{
- std::uint16_t len;
- return get_number(input_format_t::msgpack, len) and
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) &&
get_binary(input_format_t::msgpack, len, result);
}
case 0xC6: // bin 32
{
- std::uint32_t len;
- return get_number(input_format_t::msgpack, len) and
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) &&
get_binary(input_format_t::msgpack, len, result);
}
case 0xC7: // ext 8
{
- std::uint8_t len;
- std::int8_t subtype;
- return get_number(input_format_t::msgpack, len) and
- get_number(input_format_t::msgpack, subtype) and
- get_binary(input_format_t::msgpack, len, result) and
+ std::uint8_t len{};
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, len, result) &&
assign_and_return_true(subtype);
}
case 0xC8: // ext 16
{
- std::uint16_t len;
- std::int8_t subtype;
- return get_number(input_format_t::msgpack, len) and
- get_number(input_format_t::msgpack, subtype) and
- get_binary(input_format_t::msgpack, len, result) and
+ std::uint16_t len{};
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, len, result) &&
assign_and_return_true(subtype);
}
case 0xC9: // ext 32
{
- std::uint32_t len;
- std::int8_t subtype;
- return get_number(input_format_t::msgpack, len) and
- get_number(input_format_t::msgpack, subtype) and
- get_binary(input_format_t::msgpack, len, result) and
+ std::uint32_t len{};
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, len, result) &&
assign_and_return_true(subtype);
}
case 0xD4: // fixext 1
{
- std::int8_t subtype;
- return get_number(input_format_t::msgpack, subtype) and
- get_binary(input_format_t::msgpack, 1, result) and
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 1, result) &&
assign_and_return_true(subtype);
}
case 0xD5: // fixext 2
{
- std::int8_t subtype;
- return get_number(input_format_t::msgpack, subtype) and
- get_binary(input_format_t::msgpack, 2, result) and
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 2, result) &&
assign_and_return_true(subtype);
}
case 0xD6: // fixext 4
{
- std::int8_t subtype;
- return get_number(input_format_t::msgpack, subtype) and
- get_binary(input_format_t::msgpack, 4, result) and
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 4, result) &&
assign_and_return_true(subtype);
}
case 0xD7: // fixext 8
{
- std::int8_t subtype;
- return get_number(input_format_t::msgpack, subtype) and
- get_binary(input_format_t::msgpack, 8, result) and
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 8, result) &&
assign_and_return_true(subtype);
}
case 0xD8: // fixext 16
{
- std::int8_t subtype;
- return get_number(input_format_t::msgpack, subtype) and
- get_binary(input_format_t::msgpack, 16, result) and
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 16, result) &&
assign_and_return_true(subtype);
}
@@ -7357,14 +9884,14 @@ class binary_reader
*/
bool get_msgpack_array(const std::size_t len)
{
- if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len)))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
{
return false;
}
for (std::size_t i = 0; i < len; ++i)
{
- if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal()))
+ if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
{
return false;
}
@@ -7379,7 +9906,7 @@ class binary_reader
*/
bool get_msgpack_object(const std::size_t len)
{
- if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len)))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
{
return false;
}
@@ -7388,12 +9915,12 @@ class binary_reader
for (std::size_t i = 0; i < len; ++i)
{
get();
- if (JSON_HEDLEY_UNLIKELY(not get_msgpack_string(key) or not sax->key(key)))
+ if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))
{
return false;
}
- if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal()))
+ if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
{
return false;
}
@@ -7440,7 +9967,7 @@ class binary_reader
get(); // TODO(niels): may we ignore N here?
}
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value")))
{
return false;
}
@@ -7449,37 +9976,37 @@ class binary_reader
{
case 'U':
{
- std::uint8_t len;
- return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
+ std::uint8_t len{};
+ return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
}
case 'i':
{
- std::int8_t len;
- return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
+ std::int8_t len{};
+ return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
}
case 'I':
{
- std::int16_t len;
- return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
+ std::int16_t len{};
+ return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
}
case 'l':
{
- std::int32_t len;
- return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
+ std::int32_t len{};
+ return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
}
case 'L':
{
- std::int64_t len;
- return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);
+ std::int64_t len{};
+ return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);
}
default:
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType()));
}
}
@@ -7493,8 +10020,8 @@ class binary_reader
{
case 'U':
{
- std::uint8_t number;
- if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))
+ std::uint8_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
{
return false;
}
@@ -7504,19 +10031,19 @@ class binary_reader
case 'i':
{
- std::int8_t number;
- if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))
+ std::int8_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
{
return false;
}
- result = static_cast<std::size_t>(number);
+ result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
return true;
}
case 'I':
{
- std::int16_t number;
- if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))
+ std::int16_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
{
return false;
}
@@ -7526,8 +10053,8 @@ class binary_reader
case 'l':
{
- std::int32_t number;
- if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))
+ std::int32_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
{
return false;
}
@@ -7537,8 +10064,8 @@ class binary_reader
case 'L':
{
- std::int64_t number;
- if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number)))
+ std::int64_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))
{
return false;
}
@@ -7549,7 +10076,7 @@ class binary_reader
default:
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType()));
}
}
}
@@ -7574,7 +10101,7 @@ class binary_reader
if (current == '$')
{
result.second = get(); // must not ignore 'N', because 'N' maybe the type
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "type")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type")))
{
return false;
}
@@ -7582,12 +10109,12 @@ class binary_reader
get_ignore_noop();
if (JSON_HEDLEY_UNLIKELY(current != '#'))
{
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value")))
{
return false;
}
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType()));
}
return get_ubjson_size_value(result.first);
@@ -7622,57 +10149,62 @@ class binary_reader
case 'U':
{
- std::uint8_t number;
- return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number);
+ std::uint8_t number{};
+ return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number);
}
case 'i':
{
- std::int8_t number;
- return get_number(input_format_t::ubjson, number) and sax->number_integer(number);
+ std::int8_t number{};
+ return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
}
case 'I':
{
- std::int16_t number;
- return get_number(input_format_t::ubjson, number) and sax->number_integer(number);
+ std::int16_t number{};
+ return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
}
case 'l':
{
- std::int32_t number;
- return get_number(input_format_t::ubjson, number) and sax->number_integer(number);
+ std::int32_t number{};
+ return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
}
case 'L':
{
- std::int64_t number;
- return get_number(input_format_t::ubjson, number) and sax->number_integer(number);
+ std::int64_t number{};
+ return get_number(input_format_t::ubjson, number) && sax->number_integer(number);
}
case 'd':
{
- float number;
- return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), "");
+ float number{};
+ return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), "");
}
case 'D':
{
- double number;
- return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), "");
+ double number{};
+ return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 'H':
+ {
+ return get_ubjson_high_precision_number();
}
case 'C': // char
{
get();
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "char")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char")))
{
return false;
}
if (JSON_HEDLEY_UNLIKELY(current > 127))
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType()));
}
string_t s(1, static_cast<typename string_t::value_type>(current));
return sax->string(s);
@@ -7681,7 +10213,7 @@ class binary_reader
case 'S': // string
{
string_t s;
- return get_ubjson_string(s) and sax->string(s);
+ return get_ubjson_string(s) && sax->string(s);
}
case '[': // array
@@ -7693,7 +10225,7 @@ class binary_reader
default: // anything else
{
auto last_token = get_token_string();
- return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value")));
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
}
}
}
@@ -7704,14 +10236,14 @@ class binary_reader
bool get_ubjson_array()
{
std::pair<std::size_t, char_int_type> size_and_type;
- if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type)))
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
{
return false;
}
if (size_and_type.first != string_t::npos)
{
- if (JSON_HEDLEY_UNLIKELY(not sax->start_array(size_and_type.first)))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))
{
return false;
}
@@ -7722,7 +10254,7 @@ class binary_reader
{
for (std::size_t i = 0; i < size_and_type.first; ++i)
{
- if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second)))
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
{
return false;
}
@@ -7733,7 +10265,7 @@ class binary_reader
{
for (std::size_t i = 0; i < size_and_type.first; ++i)
{
- if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal()))
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
{
return false;
}
@@ -7742,14 +10274,14 @@ class binary_reader
}
else
{
- if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1))))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))
{
return false;
}
while (current != ']')
{
- if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal(false)))
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))
{
return false;
}
@@ -7766,7 +10298,7 @@ class binary_reader
bool get_ubjson_object()
{
std::pair<std::size_t, char_int_type> size_and_type;
- if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type)))
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
{
return false;
}
@@ -7774,7 +10306,7 @@ class binary_reader
string_t key;
if (size_and_type.first != string_t::npos)
{
- if (JSON_HEDLEY_UNLIKELY(not sax->start_object(size_and_type.first)))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))
{
return false;
}
@@ -7783,11 +10315,11 @@ class binary_reader
{
for (std::size_t i = 0; i < size_and_type.first; ++i)
{
- if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key)))
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
{
return false;
}
- if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second)))
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
{
return false;
}
@@ -7798,11 +10330,11 @@ class binary_reader
{
for (std::size_t i = 0; i < size_and_type.first; ++i)
{
- if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key)))
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
{
return false;
}
- if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal()))
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
{
return false;
}
@@ -7812,18 +10344,18 @@ class binary_reader
}
else
{
- if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1))))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))
{
return false;
}
while (current != '}')
{
- if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key)))
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))
{
return false;
}
- if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal()))
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
{
return false;
}
@@ -7838,6 +10370,55 @@ class binary_reader
// Note, no reader for UBJSON binary types is implemented because they do
// not exist
+ bool get_ubjson_high_precision_number()
+ {
+ // get size of following number string
+ std::size_t size{};
+ auto res = get_ubjson_size_value(size);
+ if (JSON_HEDLEY_UNLIKELY(!res))
+ {
+ return res;
+ }
+
+ // get number string
+ std::vector<char> number_vector;
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number")))
+ {
+ return false;
+ }
+ number_vector.push_back(static_cast<char>(current));
+ }
+
+ // parse number string
+ using ia_type = decltype(detail::input_adapter(number_vector));
+ auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);
+ const auto result_number = number_lexer.scan();
+ const auto number_string = number_lexer.get_token_string();
+ const auto result_remainder = number_lexer.scan();
+
+ using token_type = typename detail::lexer_base<BasicJsonType>::token_type;
+
+ if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
+ {
+ return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType()));
+ }
+
+ switch (result_number)
+ {
+ case token_type::value_integer:
+ return sax->number_integer(number_lexer.get_number_integer());
+ case token_type::value_unsigned:
+ return sax->number_unsigned(number_lexer.get_number_unsigned());
+ case token_type::value_float:
+ return sax->number_float(number_lexer.get_number_float(), std::move(number_string));
+ default:
+ return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType()));
+ }
+ }
+
///////////////////////
// Utility functions //
///////////////////////
@@ -7888,11 +10469,11 @@ class binary_reader
bool get_number(const input_format_t format, NumberType& result)
{
// step 1: read input into array with system's byte order
- std::array<std::uint8_t, sizeof(NumberType)> vec;
+ std::array<std::uint8_t, sizeof(NumberType)> vec{};
for (std::size_t i = 0; i < sizeof(NumberType); ++i)
{
get();
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "number")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
{
return false;
}
@@ -7933,15 +10514,16 @@ class binary_reader
string_t& result)
{
bool success = true;
- std::generate_n(std::back_inserter(result), len, [this, &success, &format]()
+ for (NumberType i = 0; i < len; i++)
{
get();
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "string")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string")))
{
success = false;
+ break;
}
- return std::char_traits<char_type>::to_char_type(current);
- });
+ result.push_back(static_cast<typename string_t::value_type>(current));
+ }
return success;
}
@@ -7965,15 +10547,16 @@ class binary_reader
binary_t& result)
{
bool success = true;
- std::generate_n(std::back_inserter(result), len, [this, &success, &format]()
+ for (NumberType i = 0; i < len; i++)
{
get();
- if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "binary")))
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary")))
{
success = false;
+ break;
}
- return static_cast<std::uint8_t>(current);
- });
+ result.push_back(static_cast<std::uint8_t>(current));
+ }
return success;
}
@@ -7988,7 +10571,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))
{
return sax->parse_error(chars_read, "<end of file>",
- parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context)));
+ parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType()));
}
return true;
}
@@ -7999,7 +10582,7 @@ class binary_reader
std::string get_token_string() const
{
std::array<char, 3> cr{{}};
- (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current));
+ (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
return std::string{cr.data()};
}
@@ -8034,7 +10617,7 @@ class binary_reader
break;
default: // LCOV_EXCL_LINE
- assert(false); // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
return error_msg + " " + context + ": " + detail;
@@ -8063,1542 +10646,9 @@ class binary_reader
// #include <nlohmann/detail/input/lexer.hpp>
-
-#include <array> // array
-#include <clocale> // localeconv
-#include <cstddef> // size_t
-#include <cstdio> // snprintf
-#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
-#include <initializer_list> // initializer_list
-#include <string> // char_traits, string
-#include <utility> // move
-#include <vector> // vector
-
-// #include <nlohmann/detail/input/input_adapters.hpp>
-
-// #include <nlohmann/detail/input/position_t.hpp>
-
-// #include <nlohmann/detail/macro_scope.hpp>
-
-
-namespace nlohmann
-{
-namespace detail
-{
-///////////
-// lexer //
-///////////
-
-template<typename BasicJsonType>
-class lexer_base
-{
- public:
- /// token types for the parser
- enum class token_type
- {
- uninitialized, ///< indicating the scanner is uninitialized
- literal_true, ///< the `true` literal
- literal_false, ///< the `false` literal
- literal_null, ///< the `null` literal
- value_string, ///< a string -- use get_string() for actual value
- value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value
- value_integer, ///< a signed integer -- use get_number_integer() for actual value
- value_float, ///< an floating point number -- use get_number_float() for actual value
- begin_array, ///< the character for array begin `[`
- begin_object, ///< the character for object begin `{`
- end_array, ///< the character for array end `]`
- end_object, ///< the character for object end `}`
- name_separator, ///< the name separator `:`
- value_separator, ///< the value separator `,`
- parse_error, ///< indicating a parse error
- end_of_input, ///< indicating the end of the input buffer
- literal_or_value ///< a literal or the begin of a value (only for diagnostics)
- };
-
- /// return name of values of type token_type (only used for errors)
- JSON_HEDLEY_RETURNS_NON_NULL
- JSON_HEDLEY_CONST
- static const char* token_type_name(const token_type t) noexcept
- {
- switch (t)
- {
- case token_type::uninitialized:
- return "<uninitialized>";
- case token_type::literal_true:
- return "true literal";
- case token_type::literal_false:
- return "false literal";
- case token_type::literal_null:
- return "null literal";
- case token_type::value_string:
- return "string literal";
- case token_type::value_unsigned:
- case token_type::value_integer:
- case token_type::value_float:
- return "number literal";
- case token_type::begin_array:
- return "'['";
- case token_type::begin_object:
- return "'{'";
- case token_type::end_array:
- return "']'";
- case token_type::end_object:
- return "'}'";
- case token_type::name_separator:
- return "':'";
- case token_type::value_separator:
- return "','";
- case token_type::parse_error:
- return "<parse error>";
- case token_type::end_of_input:
- return "end of input";
- case token_type::literal_or_value:
- return "'[', '{', or a literal";
- // LCOV_EXCL_START
- default: // catch non-enum values
- return "unknown token";
- // LCOV_EXCL_STOP
- }
- }
-};
-/*!
-@brief lexical analysis
-
-This class organizes the lexical analysis during JSON deserialization.
-*/
-template<typename BasicJsonType, typename InputAdapterType>
-class lexer : public lexer_base<BasicJsonType>
-{
- 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 char_type = typename InputAdapterType::char_type;
- using char_int_type = typename std::char_traits<char_type>::int_type;
-
- public:
- using token_type = typename lexer_base<BasicJsonType>::token_type;
-
- explicit lexer(InputAdapterType&& adapter)
- : ia(std::move(adapter)), decimal_point_char(static_cast<char_int_type>(get_decimal_point())) {}
-
- // delete because of pointer members
- lexer(const lexer&) = delete;
- lexer(lexer&&) = default;
- lexer& operator=(lexer&) = delete;
- lexer& operator=(lexer&&) = default;
- ~lexer() = default;
-
- private:
- /////////////////////
- // locales
- /////////////////////
-
- /// return the locale-dependent decimal point
- JSON_HEDLEY_PURE
- static char get_decimal_point() noexcept
- {
- const auto loc = localeconv();
- assert(loc != nullptr);
- return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
- }
-
- /////////////////////
- // scan functions
- /////////////////////
-
- /*!
- @brief get codepoint from 4 hex characters following `\u`
-
- For input "\u c1 c2 c3 c4" the codepoint is:
- (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
- = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
-
- Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
- must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
- conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
- between the ASCII value of the character and the desired integer value.
-
- @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
- non-hex character)
- */
- int get_codepoint()
- {
- // this function only makes sense after reading `\u`
- assert(current == 'u');
- int codepoint = 0;
-
- const auto factors = { 12u, 8u, 4u, 0u };
- for (const auto factor : factors)
- {
- get();
-
- if (current >= '0' and current <= '9')
- {
- codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);
- }
- else if (current >= 'A' and current <= 'F')
- {
- codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);
- }
- else if (current >= 'a' and current <= 'f')
- {
- codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);
- }
- else
- {
- return -1;
- }
- }
-
- assert(0x0000 <= codepoint and codepoint <= 0xFFFF);
- return codepoint;
- }
-
- /*!
- @brief check if the next byte(s) are inside a given range
-
- Adds the current byte and, for each passed range, reads a new byte and
- checks if it is inside the range. If a violation was detected, set up an
- error message and return false. Otherwise, return true.
-
- @param[in] ranges list of integers; interpreted as list of pairs of
- inclusive lower and upper bound, respectively
-
- @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
- 1, 2, or 3 pairs. This precondition is enforced by an assertion.
-
- @return true if and only if no range violation was detected
- */
- bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
- {
- assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
- add(current);
-
- for (auto range = ranges.begin(); range != ranges.end(); ++range)
- {
- get();
- if (JSON_HEDLEY_LIKELY(*range <= current and current <= *(++range)))
- {
- add(current);
- }
- else
- {
- error_message = "invalid string: ill-formed UTF-8 byte";
- return false;
- }
- }
-
- return true;
- }
-
- /*!
- @brief scan a string literal
-
- This function scans a string according to Sect. 7 of RFC 7159. While
- scanning, bytes are escaped and copied into buffer token_buffer. Then the
- function returns successfully, token_buffer is *not* null-terminated (as it
- may contain \0 bytes), and token_buffer.size() is the number of bytes in the
- string.
-
- @return token_type::value_string if string could be successfully scanned,
- token_type::parse_error otherwise
-
- @note In case of errors, variable error_message contains a textual
- description.
- */
- token_type scan_string()
- {
- // reset token_buffer (ignore opening quote)
- reset();
-
- // we entered the function by reading an open quote
- assert(current == '\"');
-
- while (true)
- {
- // get next character
- switch (get())
- {
- // end of file while parsing string
- case std::char_traits<char_type>::eof():
- {
- error_message = "invalid string: missing closing quote";
- return token_type::parse_error;
- }
-
- // closing quote
- case '\"':
- {
- return token_type::value_string;
- }
-
- // escapes
- case '\\':
- {
- switch (get())
- {
- // quotation mark
- case '\"':
- add('\"');
- break;
- // reverse solidus
- case '\\':
- add('\\');
- break;
- // solidus
- case '/':
- add('/');
- break;
- // backspace
- case 'b':
- add('\b');
- break;
- // form feed
- case 'f':
- add('\f');
- break;
- // line feed
- case 'n':
- add('\n');
- break;
- // carriage return
- case 'r':
- add('\r');
- break;
- // tab
- case 't':
- add('\t');
- break;
-
- // unicode escapes
- case 'u':
- {
- const int codepoint1 = get_codepoint();
- int codepoint = codepoint1; // start with codepoint1
-
- if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))
- {
- error_message = "invalid string: '\\u' must be followed by 4 hex digits";
- return token_type::parse_error;
- }
-
- // check if code point is a high surrogate
- if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF)
- {
- // expect next \uxxxx entry
- if (JSON_HEDLEY_LIKELY(get() == '\\' and get() == 'u'))
- {
- const int codepoint2 = get_codepoint();
-
- if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
- {
- error_message = "invalid string: '\\u' must be followed by 4 hex digits";
- return token_type::parse_error;
- }
-
- // check if codepoint2 is a low surrogate
- if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF))
- {
- // overwrite codepoint
- codepoint = static_cast<int>(
- // high surrogate occupies the most significant 22 bits
- (static_cast<unsigned int>(codepoint1) << 10u)
- // low surrogate occupies the least significant 15 bits
- + static_cast<unsigned int>(codepoint2)
- // there is still the 0xD800, 0xDC00 and 0x10000 noise
- // in the result so we have to subtract with:
- // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
- - 0x35FDC00u);
- }
- else
- {
- error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
- return token_type::parse_error;
- }
- }
- else
- {
- error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
- return token_type::parse_error;
- }
- }
- else
- {
- if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF))
- {
- error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
- return token_type::parse_error;
- }
- }
-
- // result of the above calculation yields a proper codepoint
- assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
-
- // translate codepoint into bytes
- if (codepoint < 0x80)
- {
- // 1-byte characters: 0xxxxxxx (ASCII)
- add(static_cast<char_int_type>(codepoint));
- }
- else if (codepoint <= 0x7FF)
- {
- // 2-byte characters: 110xxxxx 10xxxxxx
- add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));
- add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
- }
- else if (codepoint <= 0xFFFF)
- {
- // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
- add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));
- add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
- add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
- }
- else
- {
- // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));
- add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));
- add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
- add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
- }
-
- break;
- }
-
- // other characters after escape
- default:
- error_message = "invalid string: forbidden character after backslash";
- return token_type::parse_error;
- }
-
- break;
- }
-
- // invalid control characters
- case 0x00:
- {
- error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
- return token_type::parse_error;
- }
-
- case 0x01:
- {
- error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
- return token_type::parse_error;
- }
-
- case 0x02:
- {
- error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
- return token_type::parse_error;
- }
-
- case 0x03:
- {
- error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
- return token_type::parse_error;
- }
-
- case 0x04:
- {
- error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
- return token_type::parse_error;
- }
-
- case 0x05:
- {
- error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
- return token_type::parse_error;
- }
-
- case 0x06:
- {
- error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
- return token_type::parse_error;
- }
-
- case 0x07:
- {
- error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
- return token_type::parse_error;
- }
-
- case 0x08:
- {
- error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
- return token_type::parse_error;
- }
-
- case 0x09:
- {
- error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
- return token_type::parse_error;
- }
-
- case 0x0A:
- {
- error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
- return token_type::parse_error;
- }
-
- case 0x0B:
- {
- error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
- return token_type::parse_error;
- }
-
- case 0x0C:
- {
- error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
- return token_type::parse_error;
- }
-
- case 0x0D:
- {
- error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
- return token_type::parse_error;
- }
-
- case 0x0E:
- {
- error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
- return token_type::parse_error;
- }
-
- case 0x0F:
- {
- error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
- return token_type::parse_error;
- }
-
- case 0x10:
- {
- error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
- return token_type::parse_error;
- }
-
- case 0x11:
- {
- error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
- return token_type::parse_error;
- }
-
- case 0x12:
- {
- error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
- return token_type::parse_error;
- }
-
- case 0x13:
- {
- error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
- return token_type::parse_error;
- }
-
- case 0x14:
- {
- error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
- return token_type::parse_error;
- }
-
- case 0x15:
- {
- error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
- return token_type::parse_error;
- }
-
- case 0x16:
- {
- error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
- return token_type::parse_error;
- }
-
- case 0x17:
- {
- error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
- return token_type::parse_error;
- }
-
- case 0x18:
- {
- error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
- return token_type::parse_error;
- }
-
- case 0x19:
- {
- error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
- return token_type::parse_error;
- }
-
- case 0x1A:
- {
- error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
- return token_type::parse_error;
- }
-
- case 0x1B:
- {
- error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
- return token_type::parse_error;
- }
-
- case 0x1C:
- {
- error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
- return token_type::parse_error;
- }
-
- case 0x1D:
- {
- error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
- return token_type::parse_error;
- }
-
- case 0x1E:
- {
- error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
- return token_type::parse_error;
- }
-
- case 0x1F:
- {
- error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
- return token_type::parse_error;
- }
-
- // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
- case 0x20:
- case 0x21:
- case 0x23:
- case 0x24:
- case 0x25:
- case 0x26:
- case 0x27:
- case 0x28:
- case 0x29:
- case 0x2A:
- case 0x2B:
- case 0x2C:
- case 0x2D:
- case 0x2E:
- case 0x2F:
- case 0x30:
- case 0x31:
- case 0x32:
- case 0x33:
- case 0x34:
- case 0x35:
- case 0x36:
- case 0x37:
- case 0x38:
- case 0x39:
- case 0x3A:
- case 0x3B:
- case 0x3C:
- case 0x3D:
- case 0x3E:
- case 0x3F:
- case 0x40:
- case 0x41:
- case 0x42:
- case 0x43:
- case 0x44:
- case 0x45:
- case 0x46:
- case 0x47:
- case 0x48:
- case 0x49:
- case 0x4A:
- case 0x4B:
- case 0x4C:
- case 0x4D:
- case 0x4E:
- case 0x4F:
- case 0x50:
- case 0x51:
- case 0x52:
- case 0x53:
- case 0x54:
- case 0x55:
- case 0x56:
- case 0x57:
- case 0x58:
- case 0x59:
- case 0x5A:
- case 0x5B:
- case 0x5D:
- case 0x5E:
- case 0x5F:
- case 0x60:
- case 0x61:
- case 0x62:
- case 0x63:
- case 0x64:
- case 0x65:
- case 0x66:
- case 0x67:
- case 0x68:
- case 0x69:
- case 0x6A:
- case 0x6B:
- case 0x6C:
- case 0x6D:
- case 0x6E:
- case 0x6F:
- case 0x70:
- case 0x71:
- case 0x72:
- case 0x73:
- case 0x74:
- case 0x75:
- case 0x76:
- case 0x77:
- case 0x78:
- case 0x79:
- case 0x7A:
- case 0x7B:
- case 0x7C:
- case 0x7D:
- case 0x7E:
- case 0x7F:
- {
- add(current);
- break;
- }
-
- // U+0080..U+07FF: bytes C2..DF 80..BF
- case 0xC2:
- case 0xC3:
- case 0xC4:
- case 0xC5:
- case 0xC6:
- case 0xC7:
- case 0xC8:
- case 0xC9:
- case 0xCA:
- case 0xCB:
- case 0xCC:
- case 0xCD:
- case 0xCE:
- case 0xCF:
- case 0xD0:
- case 0xD1:
- case 0xD2:
- case 0xD3:
- case 0xD4:
- case 0xD5:
- case 0xD6:
- case 0xD7:
- case 0xD8:
- case 0xD9:
- case 0xDA:
- case 0xDB:
- case 0xDC:
- case 0xDD:
- case 0xDE:
- case 0xDF:
- {
- if (JSON_HEDLEY_UNLIKELY(not next_byte_in_range({0x80, 0xBF})))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
- case 0xE0:
- {
- if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
- // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
- case 0xE1:
- case 0xE2:
- case 0xE3:
- case 0xE4:
- case 0xE5:
- case 0xE6:
- case 0xE7:
- case 0xE8:
- case 0xE9:
- case 0xEA:
- case 0xEB:
- case 0xEC:
- case 0xEE:
- case 0xEF:
- {
- if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+D000..U+D7FF: bytes ED 80..9F 80..BF
- case 0xED:
- {
- if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
- case 0xF0:
- {
- if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
- case 0xF1:
- case 0xF2:
- case 0xF3:
- {
- if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
- case 0xF4:
- {
- if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
- {
- return token_type::parse_error;
- }
- break;
- }
-
- // remaining bytes (80..C1 and F5..FF) are ill-formed
- default:
- {
- error_message = "invalid string: ill-formed UTF-8 byte";
- return token_type::parse_error;
- }
- }
- }
- }
-
- JSON_HEDLEY_NON_NULL(2)
- static void strtof(float& f, const char* str, char** endptr) noexcept
- {
- f = std::strtof(str, endptr);
- }
-
- JSON_HEDLEY_NON_NULL(2)
- static void strtof(double& f, const char* str, char** endptr) noexcept
- {
- f = std::strtod(str, endptr);
- }
-
- JSON_HEDLEY_NON_NULL(2)
- static void strtof(long double& f, const char* str, char** endptr) noexcept
- {
- f = std::strtold(str, endptr);
- }
-
- /*!
- @brief scan a number literal
-
- This function scans a string according to Sect. 6 of RFC 7159.
-
- The function is realized with a deterministic finite state machine derived
- from the grammar described in RFC 7159. Starting in state "init", the
- input is read and used to determined the next state. Only state "done"
- accepts the number. State "error" is a trap state to model errors. In the
- table below, "anything" means any character but the ones listed before.
-
- state | 0 | 1-9 | e E | + | - | . | anything
- ---------|----------|----------|----------|---------|---------|----------|-----------
- init | zero | any1 | [error] | [error] | minus | [error] | [error]
- minus | zero | any1 | [error] | [error] | [error] | [error] | [error]
- zero | done | done | exponent | done | done | decimal1 | done
- any1 | any1 | any1 | exponent | done | done | decimal1 | done
- decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error]
- decimal2 | decimal2 | decimal2 | exponent | done | done | done | done
- exponent | any2 | any2 | [error] | sign | sign | [error] | [error]
- sign | any2 | any2 | [error] | [error] | [error] | [error] | [error]
- any2 | any2 | any2 | done | done | done | done | done
-
- The state machine is realized with one label per state (prefixed with
- "scan_number_") and `goto` statements between them. The state machine
- contains cycles, but any cycle can be left when EOF is read. Therefore,
- the function is guaranteed to terminate.
-
- During scanning, the read bytes are stored in token_buffer. This string is
- then converted to a signed integer, an unsigned integer, or a
- floating-point number.
-
- @return token_type::value_unsigned, token_type::value_integer, or
- token_type::value_float if number could be successfully scanned,
- token_type::parse_error otherwise
-
- @note The scanner is independent of the current locale. Internally, the
- locale's decimal point is used instead of `.` to work with the
- locale-dependent converters.
- */
- token_type scan_number() // lgtm [cpp/use-of-goto]
- {
- // reset token_buffer to store the number's bytes
- reset();
-
- // the type of the parsed number; initially set to unsigned; will be
- // changed if minus sign, decimal point or exponent is read
- token_type number_type = token_type::value_unsigned;
-
- // state (init): we just found out we need to scan a number
- switch (current)
- {
- case '-':
- {
- add(current);
- goto scan_number_minus;
- }
-
- case '0':
- {
- add(current);
- goto scan_number_zero;
- }
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any1;
- }
-
- // all other characters are rejected outside scan_number()
- default: // LCOV_EXCL_LINE
- assert(false); // LCOV_EXCL_LINE
- }
-
-scan_number_minus:
- // state: we just parsed a leading minus sign
- number_type = token_type::value_integer;
- switch (get())
- {
- case '0':
- {
- add(current);
- goto scan_number_zero;
- }
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any1;
- }
-
- default:
- {
- error_message = "invalid number; expected digit after '-'";
- return token_type::parse_error;
- }
- }
-
-scan_number_zero:
- // state: we just parse a zero (maybe with a leading minus sign)
- switch (get())
- {
- case '.':
- {
- add(decimal_point_char);
- goto scan_number_decimal1;
- }
-
- case 'e':
- case 'E':
- {
- add(current);
- goto scan_number_exponent;
- }
-
- default:
- goto scan_number_done;
- }
-
-scan_number_any1:
- // state: we just parsed a number 0-9 (maybe with a leading minus sign)
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any1;
- }
-
- case '.':
- {
- add(decimal_point_char);
- goto scan_number_decimal1;
- }
-
- case 'e':
- case 'E':
- {
- add(current);
- goto scan_number_exponent;
- }
-
- default:
- goto scan_number_done;
- }
-
-scan_number_decimal1:
- // state: we just parsed a decimal point
- number_type = token_type::value_float;
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_decimal2;
- }
-
- default:
- {
- error_message = "invalid number; expected digit after '.'";
- return token_type::parse_error;
- }
- }
-
-scan_number_decimal2:
- // we just parsed at least one number after a decimal point
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_decimal2;
- }
-
- case 'e':
- case 'E':
- {
- add(current);
- goto scan_number_exponent;
- }
-
- default:
- goto scan_number_done;
- }
-
-scan_number_exponent:
- // we just parsed an exponent
- number_type = token_type::value_float;
- switch (get())
- {
- case '+':
- case '-':
- {
- add(current);
- goto scan_number_sign;
- }
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any2;
- }
-
- default:
- {
- error_message =
- "invalid number; expected '+', '-', or digit after exponent";
- return token_type::parse_error;
- }
- }
-
-scan_number_sign:
- // we just parsed an exponent sign
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any2;
- }
-
- default:
- {
- error_message = "invalid number; expected digit after exponent sign";
- return token_type::parse_error;
- }
- }
-
-scan_number_any2:
- // we just parsed a number after the exponent or exponent sign
- switch (get())
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- add(current);
- goto scan_number_any2;
- }
-
- default:
- goto scan_number_done;
- }
-
-scan_number_done:
- // unget the character after the number (we only read it to know that
- // we are done scanning a number)
- unget();
-
- char* endptr = nullptr;
- errno = 0;
-
- // try to parse integers first and fall back to floats
- if (number_type == token_type::value_unsigned)
- {
- const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
-
- // we checked the number format before
- assert(endptr == token_buffer.data() + token_buffer.size());
-
- if (errno == 0)
- {
- value_unsigned = static_cast<number_unsigned_t>(x);
- if (value_unsigned == x)
- {
- return token_type::value_unsigned;
- }
- }
- }
- else if (number_type == token_type::value_integer)
- {
- const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
-
- // we checked the number format before
- assert(endptr == token_buffer.data() + token_buffer.size());
-
- if (errno == 0)
- {
- value_integer = static_cast<number_integer_t>(x);
- if (value_integer == x)
- {
- return token_type::value_integer;
- }
- }
- }
-
- // this code is reached if we parse a floating-point number or if an
- // integer conversion above failed
- strtof(value_float, token_buffer.data(), &endptr);
-
- // we checked the number format before
- assert(endptr == token_buffer.data() + token_buffer.size());
-
- return token_type::value_float;
- }
-
- /*!
- @param[in] literal_text the literal text to expect
- @param[in] length the length of the passed literal text
- @param[in] return_type the token type to return on success
- */
- JSON_HEDLEY_NON_NULL(2)
- token_type scan_literal(const char_type* literal_text, const std::size_t length,
- token_type return_type)
- {
- assert(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
- for (std::size_t i = 1; i < length; ++i)
- {
- if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))
- {
- error_message = "invalid literal";
- return token_type::parse_error;
- }
- }
- return return_type;
- }
-
- /////////////////////
- // input management
- /////////////////////
-
- /// reset token_buffer; current character is beginning of token
- void reset() noexcept
- {
- token_buffer.clear();
- token_string.clear();
- token_string.push_back(std::char_traits<char_type>::to_char_type(current));
- }
-
- /*
- @brief get next character from the input
-
- This function provides the interface to the used input adapter. It does
- not throw in case the input reached EOF, but returns a
- `std::char_traits<char>::eof()` in that case. Stores the scanned characters
- for use in error messages.
-
- @return character read from the input
- */
- char_int_type get()
- {
- ++position.chars_read_total;
- ++position.chars_read_current_line;
-
- if (next_unget)
- {
- // just reset the next_unget variable and work with current
- next_unget = false;
- }
- else
- {
- current = ia.get_character();
- }
-
- if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
- {
- token_string.push_back(std::char_traits<char_type>::to_char_type(current));
- }
-
- if (current == '\n')
- {
- ++position.lines_read;
- position.chars_read_current_line = 0;
- }
-
- return current;
- }
-
- /*!
- @brief unget current character (read it again on next get)
-
- We implement unget by setting variable next_unget to true. The input is not
- changed - we just simulate ungetting by modifying chars_read_total,
- chars_read_current_line, and token_string. The next call to get() will
- behave as if the unget character is read again.
- */
- void unget()
- {
- next_unget = true;
-
- --position.chars_read_total;
-
- // in case we "unget" a newline, we have to also decrement the lines_read
- if (position.chars_read_current_line == 0)
- {
- if (position.lines_read > 0)
- {
- --position.lines_read;
- }
- }
- else
- {
- --position.chars_read_current_line;
- }
-
- if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
- {
- assert(not token_string.empty());
- token_string.pop_back();
- }
- }
-
- /// add a character to token_buffer
- void add(char_int_type c)
- {
- token_buffer.push_back(static_cast<typename string_t::value_type>(c));
- }
-
- public:
- /////////////////////
- // value getters
- /////////////////////
-
- /// return integer value
- constexpr number_integer_t get_number_integer() const noexcept
- {
- return value_integer;
- }
-
- /// return unsigned integer value
- constexpr number_unsigned_t get_number_unsigned() const noexcept
- {
- return value_unsigned;
- }
-
- /// return floating-point value
- constexpr number_float_t get_number_float() const noexcept
- {
- return value_float;
- }
-
- /// return current string value (implicitly resets the token; useful only once)
- string_t& get_string()
- {
- return token_buffer;
- }
-
- /////////////////////
- // diagnostics
- /////////////////////
-
- /// return position of last read token
- constexpr position_t get_position() const noexcept
- {
- return position;
- }
-
- /// return the last read token (for errors only). Will never contain EOF
- /// (an arbitrary value that is not a valid char value, often -1), because
- /// 255 may legitimately occur. May contain NUL, which should be escaped.
- std::string get_token_string() const
- {
- // escape control characters
- std::string result;
- for (const auto c : token_string)
- {
- if (static_cast<unsigned char>(c) <= '\x1F')
- {
- // escape control characters
- std::array<char, 9> cs{{}};
- (std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c));
- result += cs.data();
- }
- else
- {
- // add character as is
- result.push_back(static_cast<std::string::value_type>(c));
- }
- }
-
- return result;
- }
-
- /// return syntax error message
- JSON_HEDLEY_RETURNS_NON_NULL
- constexpr const char* get_error_message() const noexcept
- {
- return error_message;
- }
-
- /////////////////////
- // actual scanner
- /////////////////////
-
- /*!
- @brief skip the UTF-8 byte order mark
- @return true iff there is no BOM or the correct BOM has been skipped
- */
- bool skip_bom()
- {
- if (get() == 0xEF)
- {
- // check if we completely parse the BOM
- return get() == 0xBB and get() == 0xBF;
- }
-
- // the first character is not the beginning of the BOM; unget it to
- // process is later
- unget();
- return true;
- }
-
- token_type scan()
- {
- // initially, skip the BOM
- if (position.chars_read_total == 0 and not skip_bom())
- {
- error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given";
- return token_type::parse_error;
- }
-
- // read next character and ignore whitespace
- do
- {
- get();
- }
- while (current == ' ' or current == '\t' or current == '\n' or current == '\r');
-
- switch (current)
- {
- // structural characters
- case '[':
- return token_type::begin_array;
- case ']':
- return token_type::end_array;
- case '{':
- return token_type::begin_object;
- case '}':
- return token_type::end_object;
- case ':':
- return token_type::name_separator;
- case ',':
- return token_type::value_separator;
-
- // literals
- case 't':
- {
- std::array<char_type, 4> true_literal = {{'t', 'r', 'u', 'e'}};
- return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);
- }
- case 'f':
- {
- std::array<char_type, 5> false_literal = {{'f', 'a', 'l', 's', 'e'}};
- return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);
- }
- case 'n':
- {
- std::array<char_type, 4> null_literal = {{'n', 'u', 'l', 'l'}};
- return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);
- }
-
- // string
- case '\"':
- return scan_string();
-
- // number
- case '-':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- return scan_number();
-
- // end of input (the null byte is needed when parsing from
- // string literals)
- case '\0':
- case std::char_traits<char_type>::eof():
- return token_type::end_of_input;
-
- // error
- default:
- error_message = "invalid literal";
- return token_type::parse_error;
- }
- }
-
- private:
- /// input adapter
- InputAdapterType ia;
-
- /// the current character
- char_int_type current = std::char_traits<char_type>::eof();
-
- /// whether the next get() call should just return current
- bool next_unget = false;
-
- /// the start position of the current token
- position_t position {};
-
- /// raw input token string (for error messages)
- std::vector<char_type> token_string {};
-
- /// buffer for variable-length tokens (numbers, strings)
- string_t token_buffer {};
-
- /// a description of occurred lexer errors
- const char* error_message = "";
-
- // number values
- number_integer_t value_integer = 0;
- number_unsigned_t value_unsigned = 0;
- number_float_t value_float = 0;
-
- /// the decimal point
- const char_int_type decimal_point_char = '.';
-};
-} // namespace detail
-} // namespace nlohmann
-
// #include <nlohmann/detail/input/parser.hpp>
-#include <cassert> // assert
#include <cmath> // isfinite
#include <cstdint> // uint8_t
#include <functional> // function
@@ -9647,7 +10697,7 @@ enum class parse_event_t : uint8_t
template<typename BasicJsonType>
using parser_callback_t =
- std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;
+ std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
/*!
@brief syntax analysis
@@ -9668,8 +10718,11 @@ class parser
/// a parser reading from an input adapter
explicit parser(InputAdapterType&& adapter,
const parser_callback_t<BasicJsonType> cb = nullptr,
- const bool allow_exceptions_ = true)
- : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_)
+ 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();
@@ -9691,15 +10744,14 @@ class parser
{
json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
sax_parse_internal(&sdp);
- result.assert_invariant();
// in strict mode, input must be completely read
- if (strict and (get_token() != token_type::end_of_input))
+ 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")));
+ exception_message(token_type::end_of_input, "value"), BasicJsonType()));
}
// in case of an error, return discarded value
@@ -9720,15 +10772,13 @@ class parser
{
json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
sax_parse_internal(&sdp);
- result.assert_invariant();
// in strict mode, input must be completely read
- if (strict and (get_token() != token_type::end_of_input))
+ 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")));
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
}
// in case of an error, return discarded value
@@ -9738,6 +10788,8 @@ class parser
return;
}
}
+
+ result.assert_invariant();
}
/*!
@@ -9752,7 +10804,7 @@ class parser
return sax_parse(&sax_acceptor, strict);
}
- template <typename SAX>
+ template<typename SAX>
JSON_HEDLEY_NON_NULL(2)
bool sax_parse(SAX* sax, const bool strict = true)
{
@@ -9760,19 +10812,18 @@ class parser
const bool result = sax_parse_internal(sax);
// strict mode: next byte must be EOF
- if (result and strict and (get_token() != token_type::end_of_input))
+ 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")));
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
}
return result;
}
private:
- template <typename SAX>
+ template<typename SAX>
JSON_HEDLEY_NON_NULL(2)
bool sax_parse_internal(SAX* sax)
{
@@ -9784,14 +10835,14 @@ class parser
while (true)
{
- if (not skip_to_state_evaluation)
+ 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(not sax->start_object(std::size_t(-1))))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))
{
return false;
}
@@ -9799,7 +10850,7 @@ class parser
// closing } -> we are done
if (get_token() == token_type::end_object)
{
- if (JSON_HEDLEY_UNLIKELY(not sax->end_object()))
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
{
return false;
}
@@ -9811,10 +10862,9 @@ class parser
{
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")));
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType()));
}
- if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string())))
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{
return false;
}
@@ -9824,8 +10874,7 @@ class parser
{
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")));
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType()));
}
// remember we are now inside an object
@@ -9838,7 +10887,7 @@ class parser
case token_type::begin_array:
{
- if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1))))
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))
{
return false;
}
@@ -9846,7 +10895,7 @@ class parser
// closing ] -> we are done
if (get_token() == token_type::end_array)
{
- if (JSON_HEDLEY_UNLIKELY(not sax->end_array()))
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
{
return false;
}
@@ -9864,14 +10913,14 @@ class parser
{
const auto res = m_lexer.get_number_float();
- if (JSON_HEDLEY_UNLIKELY(not std::isfinite(res)))
+ 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, "number overflow parsing '" + m_lexer.get_token_string() + "'"));
+ out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType()));
}
- if (JSON_HEDLEY_UNLIKELY(not sax->number_float(res, m_lexer.get_string())))
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
{
return false;
}
@@ -9881,7 +10930,7 @@ class parser
case token_type::literal_false:
{
- if (JSON_HEDLEY_UNLIKELY(not sax->boolean(false)))
+ if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
{
return false;
}
@@ -9890,7 +10939,7 @@ class parser
case token_type::literal_null:
{
- if (JSON_HEDLEY_UNLIKELY(not sax->null()))
+ if (JSON_HEDLEY_UNLIKELY(!sax->null()))
{
return false;
}
@@ -9899,7 +10948,7 @@ class parser
case token_type::literal_true:
{
- if (JSON_HEDLEY_UNLIKELY(not sax->boolean(true)))
+ if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
{
return false;
}
@@ -9908,7 +10957,7 @@ class parser
case token_type::value_integer:
{
- if (JSON_HEDLEY_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer())))
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
{
return false;
}
@@ -9917,7 +10966,7 @@ class parser
case token_type::value_string:
{
- if (JSON_HEDLEY_UNLIKELY(not sax->string(m_lexer.get_string())))
+ if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
{
return false;
}
@@ -9926,7 +10975,7 @@ class parser
case token_type::value_unsigned:
{
- if (JSON_HEDLEY_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned())))
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
{
return false;
}
@@ -9938,16 +10987,14 @@ class parser
// 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")));
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType()));
}
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")));
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType()));
}
}
}
@@ -9976,7 +11023,7 @@ class parser
// closing ]
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
{
- if (JSON_HEDLEY_UNLIKELY(not sax->end_array()))
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
{
return false;
}
@@ -9985,7 +11032,7 @@ class parser
// 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.
- assert(not states.empty());
+ JSON_ASSERT(!states.empty());
states.pop_back();
skip_to_state_evaluation = true;
continue;
@@ -9993,65 +11040,61 @@ class parser
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")));
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType()));
}
- else // 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")));
- }
- if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string())))
- {
- return false;
- }
+ // states.back() is false -> object
- // 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")));
- }
+ // 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"), BasicJsonType()));
+ }
- // parse values
- get_token();
- continue;
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+ {
+ return false;
}
- // closing }
- if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
+ // parse separator (:)
+ if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
- if (JSON_HEDLEY_UNLIKELY(not sax->end_object()))
- {
- return false;
- }
+ 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"), BasicJsonType()));
+ }
- // 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.
- assert(not states.empty());
- states.pop_back();
- skip_to_state_evaluation = true;
- continue;
+ // 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;
}
- 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")));
+ // 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"), BasicJsonType()));
}
}
@@ -10065,7 +11108,7 @@ class parser
{
std::string error_msg = "syntax error ";
- if (not context.empty())
+ if (!context.empty())
{
error_msg += "while parsing " + context + " ";
}
@@ -10100,6 +11143,7 @@ class parser
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
};
+
} // namespace detail
} // namespace nlohmann
@@ -10112,6 +11156,9 @@ class parser
#include <cstddef> // ptrdiff_t
#include <limits> // numeric_limits
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
namespace nlohmann
{
namespace detail
@@ -10132,6 +11179,7 @@ class primitive_iterator_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)();
@@ -10193,7 +11241,7 @@ class primitive_iterator_t
return *this;
}
- primitive_iterator_t const operator++(int) noexcept
+ primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type)
{
auto result = *this;
++m_it;
@@ -10206,7 +11254,7 @@ class primitive_iterator_t
return *this;
}
- primitive_iterator_t const operator--(int) noexcept
+ primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type)
{
auto result = *this;
--m_it;
@@ -10245,8 +11293,6 @@ template<typename BasicJsonType> struct internal_iterator
typename BasicJsonType::object_t::iterator object_iterator {};
/// iterator for JSON arrays
typename BasicJsonType::array_t::iterator array_iterator {};
- /// iterator for JSON binary arrays
- typename BasicJsonType::binary_t::container_type::iterator binary_iterator {};
/// generic iterator for all other types
primitive_iterator_t primitive_iterator {};
};
@@ -10259,8 +11305,6 @@ template<typename BasicJsonType> struct internal_iterator
#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
#include <type_traits> // conditional, is_const, remove_const
-// #include <nlohmann/detail/boolean_operators.hpp>
-
// #include <nlohmann/detail/exceptions.hpp>
// #include <nlohmann/detail/iterators/internal_iterator.hpp>
@@ -10303,8 +11347,10 @@ This class implements a both iterators (iterator and const_iterator) for the
template<typename BasicJsonType>
class iter_impl
{
+ /// 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 iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
+ friend other_iter_impl;
friend BasicJsonType;
friend iteration_proxy<iter_impl>;
friend iteration_proxy_value<iter_impl>;
@@ -10338,8 +11384,10 @@ class iter_impl
typename BasicJsonType::const_reference,
typename BasicJsonType::reference>::type;
- /// default constructor
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
@@ -10349,7 +11397,7 @@ class iter_impl
*/
explicit iter_impl(pointer object) noexcept : m_object(object)
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
@@ -10401,8 +11449,11 @@ class iter_impl
*/
iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
{
- m_object = other.m_object;
- m_it = other.m_it;
+ if (&other != this)
+ {
+ m_object = other.m_object;
+ m_it = other.m_it;
+ }
return *this;
}
@@ -10421,21 +11472,21 @@ class iter_impl
@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
+ 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;
}
- private:
+ 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
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
@@ -10472,7 +11523,7 @@ class iter_impl
*/
void set_end() noexcept
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
@@ -10503,24 +11554,24 @@ class iter_impl
*/
reference operator*() const
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
- assert(m_it.object_iterator != m_object->m_value.object->end());
+ JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
return m_it.object_iterator->second;
}
case value_t::array:
{
- assert(m_it.array_iterator != m_object->m_value.array->end());
+ JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
return *m_it.array_iterator;
}
case value_t::null:
- JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
default:
{
@@ -10529,7 +11580,7 @@ class iter_impl
return *m_object;
}
- JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
}
}
}
@@ -10540,19 +11591,19 @@ class iter_impl
*/
pointer operator->() const
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
- assert(m_it.object_iterator != m_object->m_value.object->end());
+ JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
return &(m_it.object_iterator->second);
}
case value_t::array:
{
- assert(m_it.array_iterator != m_object->m_value.array->end());
+ JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
return &*m_it.array_iterator;
}
@@ -10563,7 +11614,7 @@ class iter_impl
return m_object;
}
- JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
}
}
}
@@ -10572,7 +11623,7 @@ class iter_impl
@brief post-increment (it++)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
- iter_impl const operator++(int)
+ iter_impl const operator++(int) // NOLINT(readability-const-return-type)
{
auto result = *this;
++(*this);
@@ -10585,7 +11636,7 @@ class iter_impl
*/
iter_impl& operator++()
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
@@ -10615,7 +11666,7 @@ class iter_impl
@brief post-decrement (it--)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
- iter_impl const operator--(int)
+ iter_impl const operator--(int) // NOLINT(readability-const-return-type)
{
auto result = *this;
--(*this);
@@ -10628,7 +11679,7 @@ class iter_impl
*/
iter_impl& operator--()
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
@@ -10655,18 +11706,19 @@ class iter_impl
}
/*!
- @brief comparison: equal
+ @brief comparison: equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
- bool operator==(const iter_impl& other) const
+ 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"));
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object));
}
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
@@ -10682,16 +11734,17 @@ class iter_impl
}
/*!
- @brief comparison: not equal
+ @brief comparison: not equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
- bool operator!=(const iter_impl& other) const
+ 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 not operator==(other);
+ return !operator==(other);
}
/*!
- @brief comparison: smaller
+ @brief comparison: smaller
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<(const iter_impl& other) const
@@ -10699,15 +11752,15 @@ class iter_impl
// 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"));
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object));
}
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
- JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators"));
+ 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);
@@ -10718,44 +11771,44 @@ class iter_impl
}
/*!
- @brief comparison: less than or equal
+ @brief comparison: less than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<=(const iter_impl& other) const
{
- return not other.operator < (*this);
+ return !other.operator < (*this);
}
/*!
- @brief comparison: greater than
+ @brief comparison: greater than
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>(const iter_impl& other) const
{
- return not operator<=(other);
+ return !operator<=(other);
}
/*!
- @brief comparison: greater than or equal
+ @brief comparison: greater than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>=(const iter_impl& other) const
{
- return not operator<(other);
+ return !operator<(other);
}
/*!
- @brief add to iterator
+ @brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator+=(difference_type i)
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
- JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
+ JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object));
case value_t::array:
{
@@ -10774,7 +11827,7 @@ class iter_impl
}
/*!
- @brief subtract from iterator
+ @brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator-=(difference_type i)
@@ -10783,7 +11836,7 @@ class iter_impl
}
/*!
- @brief add to iterator
+ @brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl operator+(difference_type i) const
@@ -10794,7 +11847,7 @@ class iter_impl
}
/*!
- @brief addition of distance and iterator
+ @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)
@@ -10805,7 +11858,7 @@ class iter_impl
}
/*!
- @brief subtract from iterator
+ @brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl operator-(difference_type i) const
@@ -10816,17 +11869,17 @@ class iter_impl
}
/*!
- @brief return difference
+ @brief return difference
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
difference_type operator-(const iter_impl& other) const
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
- JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
+ 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;
@@ -10837,23 +11890,23 @@ class iter_impl
}
/*!
- @brief access to successor
+ @brief access to successor
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference operator[](difference_type n) const
{
- assert(m_object != nullptr);
+ JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
- JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
+ 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"));
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
default:
{
@@ -10862,29 +11915,29 @@ class iter_impl
return *m_object;
}
- JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
}
}
}
/*!
- @brief return the key of an object iterator
+ @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
{
- assert(m_object != nullptr);
+ 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"));
+ JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object));
}
/*!
- @brief return the value of an iterator
+ @brief return the value of an iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference value() const
@@ -10892,7 +11945,7 @@ class iter_impl
return operator*();
}
- private:
+ JSON_PRIVATE_UNLESS_TESTED:
/// associated JSON instance
pointer m_object = nullptr;
/// the actual iterator of the associated instance
@@ -10954,7 +12007,7 @@ class json_reverse_iterator : public std::reverse_iterator<Base>
explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
/// post-increment (it++)
- json_reverse_iterator const operator++(int)
+ json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type)
{
return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
}
@@ -10966,7 +12019,7 @@ class json_reverse_iterator : public std::reverse_iterator<Base>
}
/// post-decrement (it--)
- json_reverse_iterator const operator--(int)
+ json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type)
{
return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
}
@@ -11030,8 +12083,8 @@ class json_reverse_iterator : public std::reverse_iterator<Base>
#include <algorithm> // all_of
-#include <cassert> // assert
#include <cctype> // isdigit
+#include <limits> // max
#include <numeric> // accumulate
#include <string> // string
#include <utility> // move
@@ -11041,6 +12094,8 @@ class json_reverse_iterator : public std::reverse_iterator<Base>
// #include <nlohmann/detail/macro_scope.hpp>
+// #include <nlohmann/detail/string_escape.hpp>
+
// #include <nlohmann/detail/value_t.hpp>
@@ -11099,7 +12154,7 @@ class json_pointer
std::string{},
[](const std::string & a, const std::string & b)
{
- return a + "/" + escape(b);
+ return a + "/" + detail::escape(b);
});
}
@@ -11119,9 +12174,9 @@ class json_pointer
@complexity Linear in the length of @a ptr.
- @sa @ref operator/=(std::string) to append a reference token
- @sa @ref operator/=(std::size_t) to append an array index
- @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator
+ @sa see @ref operator/=(std::string) to append a reference token
+ @sa see @ref operator/=(std::size_t) to append an array index
+ @sa see @ref operator/(const json_pointer&, const json_pointer&) for a binary operator
@since version 3.6.0
*/
@@ -11143,9 +12198,9 @@ class json_pointer
@complexity Amortized constant.
- @sa @ref operator/=(const json_pointer&) to append a JSON pointer
- @sa @ref operator/=(std::size_t) to append an array index
- @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator
+ @sa see @ref operator/=(const json_pointer&) to append a JSON pointer
+ @sa see @ref operator/=(std::size_t) to append an array index
+ @sa see @ref operator/(const json_pointer&, std::size_t) for a binary operator
@since version 3.6.0
*/
@@ -11165,9 +12220,9 @@ class json_pointer
@complexity Amortized constant.
- @sa @ref operator/=(const json_pointer&) to append a JSON pointer
- @sa @ref operator/=(std::string) to append a reference token
- @sa @ref operator/(const json_pointer&, std::string) for a binary operator
+ @sa see @ref operator/=(const json_pointer&) to append a JSON pointer
+ @sa see @ref operator/=(std::string) to append a reference token
+ @sa see @ref operator/(const json_pointer&, std::string) for a binary operator
@since version 3.6.0
*/
@@ -11187,7 +12242,7 @@ class json_pointer
@complexity Linear in the length of @a lhs and @a rhs.
- @sa @ref operator/=(const json_pointer&) to append a JSON pointer
+ @sa see @ref operator/=(const json_pointer&) to append a JSON pointer
@since version 3.6.0
*/
@@ -11208,11 +12263,11 @@ class json_pointer
@complexity Linear in the length of @a ptr.
- @sa @ref operator/=(std::string) to append a reference token
+ @sa see @ref operator/=(std::string) to append a reference token
@since version 3.6.0
*/
- friend json_pointer operator/(const json_pointer& ptr, std::string token)
+ friend json_pointer operator/(const json_pointer& ptr, std::string token) // NOLINT(performance-unnecessary-value-param)
{
return json_pointer(ptr) /= std::move(token);
}
@@ -11228,7 +12283,7 @@ class json_pointer
@complexity Linear in the length of @a ptr.
- @sa @ref operator/=(std::size_t) to append an array index
+ @sa see @ref operator/=(std::size_t) to append an array index
@since version 3.6.0
*/
@@ -11279,7 +12334,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
- JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
}
reference_tokens.pop_back();
@@ -11303,7 +12358,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
- JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
}
return reference_tokens.back();
@@ -11357,49 +12412,60 @@ class json_pointer
@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
*/
- static int array_index(const std::string& s)
+ static typename BasicJsonType::size_type array_index(const std::string& s)
{
+ using size_type = typename BasicJsonType::size_type;
+
// error condition (cf. RFC 6901, Sect. 4)
- if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0'))
+ if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
{
- JSON_THROW(detail::parse_error::create(106, 0,
- "array index '" + s +
- "' must not begin with '0'"));
+ JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType()));
}
// error condition (cf. RFC 6901, Sect. 4)
- if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and not (s[0] >= '1' and s[0] <= '9')))
+ if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
{
- JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number"));
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType()));
}
std::size_t processed_chars = 0;
- int res = 0;
+ unsigned long long res = 0; // NOLINT(runtime/int)
JSON_TRY
{
- res = std::stoi(s, &processed_chars);
+ res = std::stoull(s, &processed_chars);
}
JSON_CATCH(std::out_of_range&)
{
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
}
// check if the string was completely read
if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))
{
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
}
- return res;
+ // 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, "array index " + s + " exceeds size_type", BasicJsonType())); // 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"));
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
}
json_pointer result = *this;
@@ -11407,6 +12473,7 @@ class json_pointer
return result;
}
+ private:
/*!
@brief create and return a reference to the pointed to value
@@ -11417,8 +12484,7 @@ class json_pointer
*/
BasicJsonType& get_and_create(BasicJsonType& j) const
{
- using size_type = typename BasicJsonType::size_type;
- auto result = &j;
+ 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
@@ -11451,7 +12517,7 @@ class json_pointer
case detail::value_t::array:
{
// create an entry in the array
- result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
+ result = &result->operator[](array_index(reference_token));
break;
}
@@ -11462,7 +12528,7 @@ class json_pointer
single value; that is, with an empty list of reference tokens.
*/
default:
- JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
+ JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j));
}
}
@@ -11490,7 +12556,6 @@ class json_pointer
*/
BasicJsonType& get_unchecked(BasicJsonType* ptr) const
{
- using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
// convert null values to arrays or objects before continuing
@@ -11505,7 +12570,7 @@ class json_pointer
});
// change value to array for numbers or "-" or to object otherwise
- *ptr = (nums or reference_token == "-")
+ *ptr = (nums || reference_token == "-")
? detail::value_t::array
: detail::value_t::object;
}
@@ -11529,14 +12594,13 @@ class json_pointer
else
{
// convert array index to number; unchecked access
- ptr = &ptr->operator[](
- static_cast<size_type>(array_index(reference_token)));
+ ptr = &ptr->operator[](array_index(reference_token));
}
break;
}
default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
@@ -11551,7 +12615,6 @@ class json_pointer
*/
BasicJsonType& get_checked(BasicJsonType* ptr) const
{
- using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
@@ -11570,16 +12633,16 @@ class json_pointer
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
- ") is out of range"));
+ ") is out of range", *ptr));
}
// note: at performs range check
- ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ ptr = &ptr->at(array_index(reference_token));
break;
}
default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
@@ -11601,7 +12664,6 @@ class json_pointer
*/
const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
{
- using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
@@ -11618,19 +12680,16 @@ class json_pointer
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" cannot be used for const access
- JSON_THROW(detail::out_of_range::create(402,
- "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
- ") is out of range"));
+ JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr));
}
// use unchecked array access
- ptr = &ptr->operator[](
- static_cast<size_type>(array_index(reference_token)));
+ ptr = &ptr->operator[](array_index(reference_token));
break;
}
default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
@@ -11645,7 +12704,6 @@ class json_pointer
*/
const BasicJsonType& get_checked(const BasicJsonType* ptr) const
{
- using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
@@ -11664,16 +12722,16 @@ class json_pointer
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
- ") is out of range"));
+ ") is out of range", *ptr));
}
// note: at performs range check
- ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ ptr = &ptr->at(array_index(reference_token));
break;
}
default:
- JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
@@ -11686,14 +12744,13 @@ class json_pointer
*/
bool contains(const BasicJsonType* ptr) const
{
- using size_type = typename BasicJsonType::size_type;
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
{
case detail::value_t::object:
{
- if (not ptr->contains(reference_token))
+ if (!ptr->contains(reference_token))
{
// we did not find the key in the object
return false;
@@ -11710,21 +12767,21 @@ class json_pointer
// "-" always fails the range check
return false;
}
- if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 and not ("0" <= reference_token and reference_token <= "9")))
+ 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(not ('1' <= reference_token[0] and reference_token[0] <= '9')))
+ 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(not ('0' <= reference_token[i] and reference_token[i] <= '9')))
+ if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
{
// other char should be between '0' and '9'
return false;
@@ -11732,7 +12789,7 @@ class json_pointer
}
}
- const auto idx = static_cast<size_type>(array_index(reference_token));
+ const auto idx = array_index(reference_token);
if (idx >= ptr->size())
{
// index out of range
@@ -11778,9 +12835,7 @@ class json_pointer
// check if nonempty reference string begins with slash
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
{
- JSON_THROW(detail::parse_error::create(107, 1,
- "JSON pointer must be empty or begin with '/' - was: '" +
- reference_string + "'"));
+ JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType()));
}
// extract the reference tokens:
@@ -11808,64 +12863,26 @@ class json_pointer
pos != std::string::npos;
pos = reference_token.find_first_of('~', pos + 1))
{
- assert(reference_token[pos] == '~');
+ JSON_ASSERT(reference_token[pos] == '~');
// ~ must be followed by 0 or 1
- if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or
- (reference_token[pos + 1] != '0' and
+ 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'"));
+ JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType()));
}
}
// finally, store the reference token
- unescape(reference_token);
+ detail::unescape(reference_token);
result.push_back(reference_token);
}
return result;
}
- /*!
- @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
- */
- static void replace_substring(std::string& s, const std::string& f,
- const std::string& t)
- {
- assert(not f.empty());
- for (auto pos = s.find(f); // find first occurrence of f
- pos != std::string::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
- {}
- }
-
- /// escape "~" to "~0" and "/" to "~1"
- static std::string escape(std::string s)
- {
- replace_substring(s, "~", "~0");
- replace_substring(s, "/", "~1");
- return s;
- }
-
- /// unescape "~1" to tilde and "~0" to slash (order is important!)
- static void unescape(std::string& s)
- {
- replace_substring(s, "~1", "/");
- replace_substring(s, "~0", "~");
- }
-
+ private:
/*!
@param[in] reference_string the reference string to the current value
@param[in] value the value to consider
@@ -11910,7 +12927,7 @@ class json_pointer
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
- flatten(reference_string + "/" + escape(element.first), element.second, result);
+ flatten(reference_string + "/" + detail::escape(element.first), element.second, result);
}
}
break;
@@ -11938,9 +12955,9 @@ class json_pointer
static BasicJsonType
unflatten(const BasicJsonType& value)
{
- if (JSON_HEDLEY_UNLIKELY(not value.is_object()))
+ if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
{
- JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
+ JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value));
}
BasicJsonType result;
@@ -11948,9 +12965,9 @@ class json_pointer
// iterate the JSON object values
for (const auto& element : *value.m_value.object)
{
- if (JSON_HEDLEY_UNLIKELY(not element.second.is_primitive()))
+ if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
{
- JSON_THROW(detail::type_error::create(315, "values in object must be 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
@@ -11994,7 +13011,7 @@ class json_pointer
friend bool operator!=(json_pointer const& lhs,
json_pointer const& rhs) noexcept
{
- return not (lhs == rhs);
+ return !(lhs == rhs);
}
/// the reference tokens
@@ -12022,26 +13039,26 @@ class json_ref
using value_type = BasicJsonType;
json_ref(value_type&& value)
- : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)
+ : owned_value(std::move(value))
{}
json_ref(const value_type& value)
- : value_ref(const_cast<value_type*>(&value)), is_rvalue(false)
+ : value_ref(&value)
{}
json_ref(std::initializer_list<json_ref> init)
- : owned_value(init), value_ref(&owned_value), is_rvalue(true)
+ : 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)...), value_ref(&owned_value),
- is_rvalue(true) {}
+ : owned_value(std::forward<Args>(args)...)
+ {}
// class should be movable only
- json_ref(json_ref&&) = default;
+ 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;
@@ -12049,33 +13066,34 @@ class json_ref
value_type moved_or_copied() const
{
- if (is_rvalue)
+ if (value_ref == nullptr)
{
- return std::move(*value_ref);
+ return std::move(owned_value);
}
return *value_ref;
}
value_type const& operator*() const
{
- return *static_cast<value_type const*>(value_ref);
+ return value_ref ? *value_ref : owned_value;
}
value_type const* operator->() const
{
- return static_cast<value_type const*>(value_ref);
+ return &** this;
}
private:
mutable value_type owned_value = nullptr;
- value_type* value_ref = nullptr;
- const bool is_rvalue;
+ value_type const* value_ref = nullptr;
};
} // namespace detail
} // namespace nlohmann
// #include <nlohmann/detail/macro_scope.hpp>
+// #include <nlohmann/detail/string_escape.hpp>
+
// #include <nlohmann/detail/meta/cpp_future.hpp>
// #include <nlohmann/detail/meta/type_traits.hpp>
@@ -12085,11 +13103,12 @@ class json_ref
#include <algorithm> // reverse
#include <array> // array
+#include <cmath> // isnan, isinf
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
#include <cstring> // memcpy
#include <limits> // numeric_limits
#include <string> // string
-#include <cmath> // isnan, isinf
+#include <utility> // move
// #include <nlohmann/detail/input/binary_reader.hpp>
@@ -12119,6 +13138,12 @@ 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
@@ -12238,6 +13263,7 @@ class binary_writer
{
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
public:
/*!
@@ -12245,9 +13271,9 @@ class binary_writer
@param[in] adapter output adapter to write to
*/
- explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
+ explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
{
- assert(oa);
+ JSON_ASSERT(oa);
}
/*!
@@ -12266,7 +13292,7 @@ class binary_writer
default:
{
- JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j));;
}
}
}
@@ -12404,18 +13430,7 @@ class binary_writer
}
else
{
- if (static_cast<double>(j.m_value.number_float) >= static_cast<double>(std::numeric_limits<float>::lowest()) and
- static_cast<double>(j.m_value.number_float) <= static_cast<double>((std::numeric_limits<float>::max)()) and
- static_cast<double>(static_cast<float>(j.m_value.number_float)) == static_cast<double>(j.m_value.number_float))
- {
- oa->write_character(get_cbor_float_prefix(static_cast<float>(j.m_value.number_float)));
- write_number(static_cast<float>(j.m_value.number_float));
- }
- else
- {
- oa->write_character(get_cbor_float_prefix(j.m_value.number_float));
- write_number(j.m_value.number_float);
- }
+ write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);
}
break;
}
@@ -12499,6 +13514,12 @@ class binary_writer
case value_t::binary:
{
+ if (j.m_value.binary->has_subtype())
+ {
+ write_number(static_cast<std::uint8_t>(0xd8));
+ write_number(j.m_value.binary->subtype());
+ }
+
// step 1: write control byte and the binary array size
const auto N = j.m_value.binary->size();
if (N <= 0x17)
@@ -12646,28 +13667,28 @@ class binary_writer
// negative fixnum
write_number(static_cast<std::int8_t>(j.m_value.number_integer));
}
- else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() and
+ else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
{
// int 8
oa->write_character(to_char_type(0xD0));
write_number(static_cast<std::int8_t>(j.m_value.number_integer));
}
- else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() and
+ else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
{
// int 16
oa->write_character(to_char_type(0xD1));
write_number(static_cast<std::int16_t>(j.m_value.number_integer));
}
- else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() and
+ else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
{
// int 32
oa->write_character(to_char_type(0xD2));
write_number(static_cast<std::int32_t>(j.m_value.number_integer));
}
- else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() and
+ else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&
j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
{
// int 64
@@ -12714,8 +13735,7 @@ class binary_writer
case value_t::number_float:
{
- oa->write_character(get_msgpack_float_prefix(j.m_value.number_float));
- write_number(j.m_value.number_float);
+ write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);
break;
}
@@ -12794,7 +13814,7 @@ class binary_writer
const auto N = j.m_value.binary->size();
if (N <= (std::numeric_limits<std::uint8_t>::max)())
{
- std::uint8_t output_type;
+ std::uint8_t output_type{};
bool fixed = true;
if (use_ext)
{
@@ -12829,37 +13849,25 @@ class binary_writer
}
oa->write_character(to_char_type(output_type));
- if (not fixed)
+ if (!fixed)
{
write_number(static_cast<std::uint8_t>(N));
}
}
else if (N <= (std::numeric_limits<std::uint16_t>::max)())
{
- std::uint8_t output_type;
- if (use_ext)
- {
- output_type = 0xC8; // ext 16
- }
- else
- {
- output_type = 0xC5; // bin 16
- }
+ std::uint8_t output_type = use_ext
+ ? 0xC8 // ext 16
+ : 0xC5; // bin 16
oa->write_character(to_char_type(output_type));
write_number(static_cast<std::uint16_t>(N));
}
else if (N <= (std::numeric_limits<std::uint32_t>::max)())
{
- std::uint8_t output_type;
- if (use_ext)
- {
- output_type = 0xC9; // ext 32
- }
- else
- {
- output_type = 0xC6; // bin 32
- }
+ std::uint8_t output_type = use_ext
+ ? 0xC9 // ext 32
+ : 0xC6; // bin 32
oa->write_character(to_char_type(output_type));
write_number(static_cast<std::uint32_t>(N));
@@ -12985,9 +13993,9 @@ class binary_writer
}
bool prefix_required = true;
- if (use_type and not j.m_value.array->empty())
+ if (use_type && !j.m_value.array->empty())
{
- assert(use_count);
+ JSON_ASSERT(use_count);
const CharType first_prefix = ubjson_prefix(j.front());
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
[this, first_prefix](const BasicJsonType & v)
@@ -13014,7 +14022,7 @@ class binary_writer
write_ubjson(el, use_count, use_type, prefix_required);
}
- if (not use_count)
+ if (!use_count)
{
oa->write_character(to_char_type(']'));
}
@@ -13029,9 +14037,9 @@ class binary_writer
oa->write_character(to_char_type('['));
}
- if (use_type and not j.m_value.binary->empty())
+ if (use_type && !j.m_value.binary->empty())
{
- assert(use_count);
+ JSON_ASSERT(use_count);
oa->write_character(to_char_type('$'));
oa->write_character('U');
}
@@ -13057,7 +14065,7 @@ class binary_writer
}
}
- if (not use_count)
+ if (!use_count)
{
oa->write_character(to_char_type(']'));
}
@@ -13073,9 +14081,9 @@ class binary_writer
}
bool prefix_required = true;
- if (use_type and not j.m_value.object->empty())
+ if (use_type && !j.m_value.object->empty())
{
- assert(use_count);
+ JSON_ASSERT(use_count);
const CharType first_prefix = ubjson_prefix(j.front());
const bool same_prefix = std::all_of(j.begin(), j.end(),
[this, first_prefix](const BasicJsonType & v)
@@ -13106,7 +14114,7 @@ class binary_writer
write_ubjson(el.second, use_count, use_type, prefix_required);
}
- if (not use_count)
+ if (!use_count)
{
oa->write_character(to_char_type('}'));
}
@@ -13128,13 +14136,12 @@ class binary_writer
@return The size of a BSON document entry header, including the id marker
and the entry name size (and its null-terminator).
*/
- static std::size_t calc_bson_entry_header_size(const string_t& name)
+ static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
{
const auto it = name.find(static_cast<typename string_t::value_type>(0));
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
{
- JSON_THROW(out_of_range::create(409,
- "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")"));
+ JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j));
}
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
@@ -13207,7 +14214,7 @@ class binary_writer
*/
static std::size_t calc_bson_integer_size(const std::int64_t value)
{
- return (std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)()
+ return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()
? sizeof(std::int32_t)
: sizeof(std::int64_t);
}
@@ -13218,7 +14225,7 @@ class binary_writer
void write_bson_integer(const string_t& name,
const std::int64_t value)
{
- if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())
+ if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())
{
write_bson_entry_header(name, 0x10); // int32
write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
@@ -13244,21 +14251,21 @@ class binary_writer
@brief Writes a BSON element with key @a name and unsigned @a value
*/
void write_bson_unsigned(const string_t& name,
- const std::uint64_t value)
+ const BasicJsonType& j)
{
- if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+ if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
{
write_bson_entry_header(name, 0x10 /* int32 */);
- write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
+ write_number<std::int32_t, true>(static_cast<std::int32_t>(j.m_value.number_unsigned));
}
- else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+ else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
{
write_bson_entry_header(name, 0x12 /* int64 */);
- write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
+ write_number<std::int64_t, true>(static_cast<std::int64_t>(j.m_value.number_unsigned));
}
else
{
- JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64"));
+ JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j));
}
}
@@ -13335,7 +14342,7 @@ class binary_writer
static std::size_t calc_bson_element_size(const string_t& name,
const BasicJsonType& j)
{
- const auto header_size = calc_bson_entry_header_size(name);
+ const auto header_size = calc_bson_entry_header_size(name, j);
switch (j.type())
{
case value_t::object:
@@ -13367,7 +14374,7 @@ class binary_writer
// LCOV_EXCL_START
default:
- assert(false);
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
return 0ul;
// LCOV_EXCL_STOP
}
@@ -13378,7 +14385,6 @@ class binary_writer
key @a name.
@param name The name to associate with the JSON entity @a j within the
current BSON document
- @return The size of the BSON entry
*/
void write_bson_element(const string_t& name,
const BasicJsonType& j)
@@ -13404,7 +14410,7 @@ class binary_writer
return write_bson_integer(name, j.m_value.number_integer);
case value_t::number_unsigned:
- return write_bson_unsigned(name, j.m_value.number_unsigned);
+ return write_bson_unsigned(name, j);
case value_t::string:
return write_bson_string(name, *j.m_value.string);
@@ -13414,7 +14420,7 @@ class binary_writer
// LCOV_EXCL_START
default:
- assert(false);
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
return;
// LCOV_EXCL_STOP
}
@@ -13423,8 +14429,8 @@ class binary_writer
/*!
@brief Calculates the size of the BSON serialization of the given
JSON-object @a j.
- @param[in] j JSON value to serialize
- @pre j.type() == value_t::object
+ @param[in] value JSON value to serialize
+ @pre value.type() == value_t::object
*/
static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
{
@@ -13438,8 +14444,8 @@ class binary_writer
}
/*!
- @param[in] j JSON value to serialize
- @pre j.type() == value_t::object
+ @param[in] value JSON value to serialize
+ @pre value.type() == value_t::object
*/
void write_bson_object(const typename BasicJsonType::object_t& value)
{
@@ -13546,18 +14552,28 @@ class binary_writer
}
else
{
- JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('H')); // high-precision number
+ }
+
+ const auto number = BasicJsonType(n).dump();
+ write_number_with_ubjson_prefix(number.size(), true);
+ for (std::size_t i = 0; i < number.size(); ++i)
+ {
+ oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+ }
}
}
// UBJSON: write number (signed integer)
- template<typename NumberType, typename std::enable_if<
- std::is_signed<NumberType>::value and
- not std::is_floating_point<NumberType>::value, int>::type = 0>
+ template < typename NumberType, typename std::enable_if <
+ std::is_signed<NumberType>::value&&
+ !std::is_floating_point<NumberType>::value, int >::type = 0 >
void write_number_with_ubjson_prefix(const NumberType n,
const bool add_prefix)
{
- if ((std::numeric_limits<std::int8_t>::min)() <= n and n <= (std::numeric_limits<std::int8_t>::max)())
+ if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())
{
if (add_prefix)
{
@@ -13565,7 +14581,7 @@ class binary_writer
}
write_number(static_cast<std::int8_t>(n));
}
- else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n and n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))
+ else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))
{
if (add_prefix)
{
@@ -13573,7 +14589,7 @@ class binary_writer
}
write_number(static_cast<std::uint8_t>(n));
}
- else if ((std::numeric_limits<std::int16_t>::min)() <= n and n <= (std::numeric_limits<std::int16_t>::max)())
+ else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())
{
if (add_prefix)
{
@@ -13581,7 +14597,7 @@ class binary_writer
}
write_number(static_cast<std::int16_t>(n));
}
- else if ((std::numeric_limits<std::int32_t>::min)() <= n and n <= (std::numeric_limits<std::int32_t>::max)())
+ else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
{
if (add_prefix)
{
@@ -13589,7 +14605,7 @@ class binary_writer
}
write_number(static_cast<std::int32_t>(n));
}
- else if ((std::numeric_limits<std::int64_t>::min)() <= n and n <= (std::numeric_limits<std::int64_t>::max)())
+ else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
{
if (add_prefix)
{
@@ -13600,19 +14616,23 @@ class binary_writer
// LCOV_EXCL_START
else
{
- JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64"));
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('H')); // high-precision number
+ }
+
+ const auto number = BasicJsonType(n).dump();
+ write_number_with_ubjson_prefix(number.size(), true);
+ for (std::size_t i = 0; i < number.size(); ++i)
+ {
+ oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+ }
}
// LCOV_EXCL_STOP
}
/*!
@brief determine the type prefix of container values
-
- @note This function does not need to be 100% accurate when it comes to
- integer limits. In case a number exceeds the limits of int64_t,
- this will be detected by a later call to function
- write_number_with_ubjson_prefix. Therefore, we return 'L' for any
- value that does not fit the previous limits.
*/
CharType ubjson_prefix(const BasicJsonType& j) const noexcept
{
@@ -13626,24 +14646,28 @@ class binary_writer
case value_t::number_integer:
{
- if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+ if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
{
return 'i';
}
- if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+ if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
{
return 'U';
}
- if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+ if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
{
return 'I';
}
- if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+ if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
{
return 'l';
}
- // no check and assume int64_t (see note above)
- return 'L';
+ if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+ {
+ return 'L';
+ }
+ // anything else is treated as high-precision number
+ return 'H'; // LCOV_EXCL_LINE
}
case value_t::number_unsigned:
@@ -13664,8 +14688,12 @@ class binary_writer
{
return 'l';
}
- // no check and assume int64_t (see note above)
- return 'L';
+ if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+ {
+ return 'L';
+ }
+ // anything else is treated as high-precision number
+ return 'H'; // LCOV_EXCL_LINE
}
case value_t::number_float:
@@ -13715,7 +14743,7 @@ class binary_writer
void write_number(const NumberType n)
{
// step 1: write number to array of length NumberType
- std::array<CharType, sizeof(NumberType)> vec;
+ std::array<CharType, sizeof(NumberType)> vec{};
std::memcpy(vec.data(), &n, sizeof(NumberType));
// step 2: write array to output (with possible reordering)
@@ -13728,20 +14756,40 @@ class binary_writer
oa->write_characters(vec.data(), sizeof(NumberType));
}
+ void write_compact_float(const number_float_t n, detail::input_format_t format)
+ {
+ if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
+ static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
+ static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))
+ {
+ oa->write_character(format == detail::input_format_t::cbor
+ ? get_cbor_float_prefix(static_cast<float>(n))
+ : get_msgpack_float_prefix(static_cast<float>(n)));
+ write_number(static_cast<float>(n));
+ }
+ else
+ {
+ oa->write_character(format == detail::input_format_t::cbor
+ ? get_cbor_float_prefix(n)
+ : get_msgpack_float_prefix(n));
+ write_number(n);
+ }
+ }
+
public:
// The following to_char_type functions are implement the conversion
// between uint8_t and CharType. In case CharType is not unsigned,
// such a conversion is required to allow values greater than 128.
// See <https://github.com/nlohmann/json/issues/1286> for a discussion.
template < typename C = CharType,
- enable_if_t < std::is_signed<C>::value and std::is_signed<char>::value > * = nullptr >
+ enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >
static constexpr CharType to_char_type(std::uint8_t x) noexcept
{
return *reinterpret_cast<char*>(&x);
}
template < typename C = CharType,
- enable_if_t < std::is_signed<C>::value and std::is_unsigned<char>::value > * = nullptr >
+ enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >
static CharType to_char_type(std::uint8_t x) noexcept
{
static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
@@ -13760,8 +14808,8 @@ class binary_writer
template < typename InputCharType, typename C = CharType,
enable_if_t <
- std::is_signed<C>::value and
- std::is_signed<char>::value and
+ std::is_signed<C>::value &&
+ std::is_signed<char>::value &&
std::is_same<char, typename std::remove_cv<InputCharType>::type>::value
> * = nullptr >
static constexpr CharType to_char_type(InputCharType x) noexcept
@@ -13786,32 +14834,26 @@ class binary_writer
#include <algorithm> // reverse, remove, fill, find, none_of
#include <array> // array
-#include <cassert> // assert
#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
+#include <string> // string, char_traits
#include <type_traits> // is_same
#include <utility> // move
-// #include <nlohmann/detail/boolean_operators.hpp>
-
// #include <nlohmann/detail/conversions/to_chars.hpp>
#include <array> // array
-#include <cassert> // assert
#include <cmath> // signbit, isfinite
#include <cstdint> // intN_t, uintN_t
#include <cstring> // memcpy, memmove
#include <limits> // numeric_limits
#include <type_traits> // conditional
-// #include <nlohmann/detail/boolean_operators.hpp>
-
// #include <nlohmann/detail/macro_scope.hpp>
@@ -13842,7 +14884,7 @@ For a detailed description of the algorithm see:
namespace dtoa_impl
{
-template <typename Target, typename Source>
+template<typename Target, typename Source>
Target reinterpret_bits(const Source source)
{
static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
@@ -13867,8 +14909,8 @@ struct diyfp // f * 2^e
*/
static diyfp sub(const diyfp& x, const diyfp& y) noexcept
{
- assert(x.e == y.e);
- assert(x.f >= y.f);
+ JSON_ASSERT(x.e == y.e);
+ JSON_ASSERT(x.f >= y.f);
return {x.f - y.f, x.e};
}
@@ -13944,7 +14986,7 @@ struct diyfp // f * 2^e
*/
static diyfp normalize(diyfp x) noexcept
{
- assert(x.f != 0);
+ JSON_ASSERT(x.f != 0);
while ((x.f >> 63u) == 0)
{
@@ -13963,8 +15005,8 @@ struct diyfp // f * 2^e
{
const int delta = x.e - target_exponent;
- assert(delta >= 0);
- assert(((x.f << delta) >> delta) == x.f);
+ JSON_ASSERT(delta >= 0);
+ JSON_ASSERT(((x.f << delta) >> delta) == x.f);
return {x.f << delta, target_exponent};
}
@@ -13983,11 +15025,11 @@ boundaries.
@pre value must be finite and positive
*/
-template <typename FloatType>
+template<typename FloatType>
boundaries compute_boundaries(FloatType value)
{
- assert(std::isfinite(value));
- assert(value > 0);
+ JSON_ASSERT(std::isfinite(value));
+ JSON_ASSERT(value > 0);
// Convert the IEEE representation into a diyfp.
//
@@ -14006,7 +15048,7 @@ boundaries compute_boundaries(FloatType value)
using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;
- const std::uint64_t bits = reinterpret_bits<bits_type>(value);
+ const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));
const std::uint64_t E = bits >> (kPrecision - 1);
const std::uint64_t F = bits & (kHiddenBit - 1);
@@ -14036,7 +15078,7 @@ boundaries compute_boundaries(FloatType value)
// -----------------+------+------+-------------+-------------+--- (B)
// v- m- v m+ v+
- const bool lower_boundary_is_closer = F == 0 and E > 1;
+ const bool lower_boundary_is_closer = F == 0 && E > 1;
const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
const diyfp m_minus = lower_boundary_is_closer
? diyfp(4 * v.f - 1, v.e - 2) // (B)
@@ -14267,18 +15309,18 @@ inline cached_power get_cached_power_for_binary_exponent(int e)
// k = ceil((kAlpha - e - 1) * 0.30102999566398114)
// for |e| <= 1500, but doesn't require floating-point operations.
// NB: log_10(2) ~= 78913 / 2^18
- assert(e >= -1500);
- assert(e <= 1500);
+ JSON_ASSERT(e >= -1500);
+ JSON_ASSERT(e <= 1500);
const int f = kAlpha - e - 1;
const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);
const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
- assert(index >= 0);
- assert(static_cast<std::size_t>(index) < kCachedPowers.size());
+ JSON_ASSERT(index >= 0);
+ JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());
const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];
- assert(kAlpha <= cached.e + e + 64);
- assert(kGamma >= cached.e + e + 64);
+ JSON_ASSERT(kAlpha <= cached.e + e + 64);
+ JSON_ASSERT(kGamma >= cached.e + e + 64);
return cached;
}
@@ -14296,60 +15338,58 @@ inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)
return 10;
}
// LCOV_EXCL_STOP
- else if (n >= 100000000)
+ if (n >= 100000000)
{
pow10 = 100000000;
return 9;
}
- else if (n >= 10000000)
+ if (n >= 10000000)
{
pow10 = 10000000;
return 8;
}
- else if (n >= 1000000)
+ if (n >= 1000000)
{
pow10 = 1000000;
return 7;
}
- else if (n >= 100000)
+ if (n >= 100000)
{
pow10 = 100000;
return 6;
}
- else if (n >= 10000)
+ if (n >= 10000)
{
pow10 = 10000;
return 5;
}
- else if (n >= 1000)
+ if (n >= 1000)
{
pow10 = 1000;
return 4;
}
- else if (n >= 100)
+ if (n >= 100)
{
pow10 = 100;
return 3;
}
- else if (n >= 10)
+ if (n >= 10)
{
pow10 = 10;
return 2;
}
- else
- {
- pow10 = 1;
- return 1;
- }
+
+ pow10 = 1;
+ return 1;
}
inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,
std::uint64_t rest, std::uint64_t ten_k)
{
- assert(len >= 1);
- assert(dist <= delta);
- assert(rest <= delta);
- assert(ten_k > 0);
+ JSON_ASSERT(len >= 1);
+ JSON_ASSERT(dist <= delta);
+ JSON_ASSERT(rest <= delta);
+ JSON_ASSERT(ten_k > 0);
// <--------------------------- delta ---->
// <---- dist --------->
@@ -14371,10 +15411,10 @@ inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t d
// integer arithmetic.
while (rest < dist
- and delta - rest >= ten_k
- and (rest + ten_k < dist or dist - rest > rest + ten_k - dist))
+ && delta - rest >= ten_k
+ && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))
{
- assert(buf[len - 1] != '0');
+ JSON_ASSERT(buf[len - 1] != '0');
buf[len - 1]--;
rest += ten_k;
}
@@ -14402,8 +15442,8 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
// Grisu2 generates the digits of M+ from left to right and stops as soon as
// V is in [M-,M+].
- assert(M_plus.e >= kAlpha);
- assert(M_plus.e <= kGamma);
+ JSON_ASSERT(M_plus.e >= kAlpha);
+ JSON_ASSERT(M_plus.e <= kGamma);
std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e)
@@ -14424,9 +15464,9 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
//
// Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
- assert(p1 > 0);
+ JSON_ASSERT(p1 > 0);
- std::uint32_t pow10;
+ std::uint32_t pow10{};
const int k = find_largest_pow10(p1, pow10);
// 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
@@ -14460,7 +15500,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
// M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
// = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
//
- assert(d <= 9);
+ JSON_ASSERT(d <= 9);
buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
//
// M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
@@ -14547,7 +15587,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
//
// and stop as soon as 10^-m * r * 2^e <= delta * 2^e
- assert(p2 > delta);
+ JSON_ASSERT(p2 > delta);
int m = 0;
for (;;)
@@ -14558,7 +15598,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
// = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e
// = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
//
- assert(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
+ JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
p2 *= 10;
const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e
const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
@@ -14567,7 +15607,7 @@ inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
// = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
// = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
//
- assert(d <= 9);
+ JSON_ASSERT(d <= 9);
buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
//
// M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
@@ -14628,8 +15668,8 @@ JSON_HEDLEY_NON_NULL(1)
inline void grisu2(char* buf, int& len, int& decimal_exponent,
diyfp m_minus, diyfp v, diyfp m_plus)
{
- assert(m_plus.e == m_minus.e);
- assert(m_plus.e == v.e);
+ JSON_ASSERT(m_plus.e == m_minus.e);
+ JSON_ASSERT(m_plus.e == v.e);
// --------(-----------------------+-----------------------)-------- (A)
// m- v m+
@@ -14683,15 +15723,15 @@ v = buf * 10^decimal_exponent
len is the length of the buffer (number of decimal digits)
The buffer must be large enough, i.e. >= max_digits10.
*/
-template <typename FloatType>
+template<typename FloatType>
JSON_HEDLEY_NON_NULL(1)
void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
{
static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
"internal error: not enough precision");
- assert(std::isfinite(value));
- assert(value > 0);
+ JSON_ASSERT(std::isfinite(value));
+ JSON_ASSERT(value > 0);
// If the neighbors (and boundaries) of 'value' are always computed for double-precision
// numbers, all float's can be recovered using strtod (and strtof). However, the resulting
@@ -14727,8 +15767,8 @@ JSON_HEDLEY_NON_NULL(1)
JSON_HEDLEY_RETURNS_NON_NULL
inline char* append_exponent(char* buf, int e)
{
- assert(e > -1000);
- assert(e < 1000);
+ JSON_ASSERT(e > -1000);
+ JSON_ASSERT(e < 1000);
if (e < 0)
{
@@ -14780,8 +15820,8 @@ JSON_HEDLEY_RETURNS_NON_NULL
inline char* format_buffer(char* buf, int len, int decimal_exponent,
int min_exp, int max_exp)
{
- assert(min_exp < 0);
- assert(max_exp > 0);
+ JSON_ASSERT(min_exp < 0);
+ JSON_ASSERT(max_exp > 0);
const int k = len;
const int n = len + decimal_exponent;
@@ -14790,7 +15830,7 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent,
// k is the length of the buffer (number of decimal digits)
// n is the position of the decimal point relative to the start of the buffer.
- if (k <= n and n <= max_exp)
+ if (k <= n && n <= max_exp)
{
// digits[000]
// len <= max_exp + 2
@@ -14802,19 +15842,19 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent,
return buf + (static_cast<size_t>(n) + 2);
}
- if (0 < n and n <= max_exp)
+ if (0 < n && n <= max_exp)
{
// dig.its
// len <= max_digits10 + 1
- assert(k > n);
+ JSON_ASSERT(k > n);
std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));
buf[n] = '.';
return buf + (static_cast<size_t>(k) + 1U);
}
- if (min_exp < n and n <= 0)
+ if (min_exp < n && n <= 0)
{
// 0.[000]digits
// len <= 2 + (-min_exp - 1) + max_digits10
@@ -14859,13 +15899,13 @@ format. Returns an iterator pointing past-the-end of the decimal representation.
@note The buffer must be large enough.
@note The result is NOT null-terminated.
*/
-template <typename FloatType>
+template<typename FloatType>
JSON_HEDLEY_NON_NULL(1, 2)
JSON_HEDLEY_RETURNS_NON_NULL
char* to_chars(char* first, const char* last, FloatType value)
{
static_cast<void>(last); // maybe unused - fix warning
- assert(std::isfinite(value));
+ JSON_ASSERT(std::isfinite(value));
// Use signbit(value) instead of (value < 0) since signbit works for -0.
if (std::signbit(value))
@@ -14883,7 +15923,7 @@ char* to_chars(char* first, const char* last, FloatType value)
return first;
}
- assert(last - first >= std::numeric_limits<FloatType>::max_digits10);
+ JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
// Compute v = buffer * 10^decimal_exponent.
// The decimal digits are stored in the buffer, which needs to be interpreted
@@ -14893,16 +15933,16 @@ char* to_chars(char* first, const char* last, FloatType value)
int decimal_exponent = 0;
dtoa_impl::grisu2(first, len, decimal_exponent, value);
- assert(len <= std::numeric_limits<FloatType>::max_digits10);
+ JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
// Format the buffer like printf("%.*g", prec, value)
constexpr int kMinExp = -4;
// Use digits10 here to increase compatibility with version 2.
constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
- assert(last - first >= kMaxExp + 2);
- assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
- assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+ JSON_ASSERT(last - first >= kMaxExp + 2);
+ JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+ JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
}
@@ -14960,8 +16000,8 @@ class serializer
error_handler_t error_handler_ = error_handler_t::strict)
: o(std::move(s))
, loc(std::localeconv())
- , thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep))
- , decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point))
+ , 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_)
@@ -15036,8 +16076,8 @@ class serializer
}
// last element
- assert(i != val.m_value.object->cend());
- assert(std::next(i) == val.m_value.object->cend());
+ JSON_ASSERT(i != val.m_value.object->cend());
+ JSON_ASSERT(std::next(i) == val.m_value.object->cend());
o->write_characters(indent_string.c_str(), new_indent);
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
@@ -15064,8 +16104,8 @@ class serializer
}
// last element
- assert(i != val.m_value.object->cend());
- assert(std::next(i) == val.m_value.object->cend());
+ JSON_ASSERT(i != val.m_value.object->cend());
+ JSON_ASSERT(std::next(i) == val.m_value.object->cend());
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2);
@@ -15106,7 +16146,7 @@ class serializer
}
// last element
- assert(not val.m_value.array->empty());
+ JSON_ASSERT(!val.m_value.array->empty());
o->write_characters(indent_string.c_str(), new_indent);
dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
@@ -15127,7 +16167,7 @@ class serializer
}
// last element
- assert(not val.m_value.array->empty());
+ JSON_ASSERT(!val.m_value.array->empty());
dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
o->write_character(']');
@@ -15161,7 +16201,7 @@ class serializer
o->write_characters("\"bytes\": [", 10);
- if (not val.m_value.binary->empty())
+ if (!val.m_value.binary->empty())
{
for (auto i = val.m_value.binary->cbegin();
i != val.m_value.binary->cend() - 1; ++i)
@@ -15192,7 +16232,7 @@ class serializer
{
o->write_characters("{\"bytes\":[", 10);
- if (not val.m_value.binary->empty())
+ if (!val.m_value.binary->empty())
{
for (auto i = val.m_value.binary->cbegin();
i != val.m_value.binary->cend() - 1; ++i)
@@ -15261,11 +16301,11 @@ class serializer
}
default: // LCOV_EXCL_LINE
- assert(false); // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
- private:
+ JSON_PRIVATE_UNLESS_TESTED:
/*!
@brief dump escaped string
@@ -15282,7 +16322,7 @@ class serializer
*/
void dump_escaped(const string_t& s, const bool ensure_ascii)
{
- std::uint32_t codepoint;
+ std::uint32_t codepoint{};
std::uint8_t state = UTF8_ACCEPT;
std::size_t bytes = 0; // number of bytes written to string_buffer
@@ -15353,16 +16393,18 @@ class serializer
{
// escape control characters (0x00..0x1F) or, if
// ensure_ascii parameter is used, non-ASCII characters
- if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))
+ if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
{
if (codepoint <= 0xFFFF)
{
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(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)
(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)));
@@ -15401,8 +16443,9 @@ class serializer
case error_handler_t::strict:
{
std::string sn(3, '\0');
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", byte);
- JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn));
+ JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType()));
}
case error_handler_t::ignore:
@@ -15460,14 +16503,14 @@ class serializer
}
default: // LCOV_EXCL_LINE
- assert(false); // 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 (not ensure_ascii)
+ if (!ensure_ascii)
{
// code point will not be escaped - copy byte to buffer
string_buffer[bytes++] = s[i];
@@ -15495,8 +16538,9 @@ class serializer
case error_handler_t::strict:
{
std::string sn(3, '\0');
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back()));
- JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn));
+ JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType()));
}
case error_handler_t::ignore:
@@ -15523,11 +16567,12 @@ class serializer
}
default: // LCOV_EXCL_LINE
- assert(false); // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
}
+ private:
/*!
@brief count digits
@@ -15571,11 +16616,11 @@ class serializer
@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_same<NumberType, number_unsigned_t>::value or
- std::is_same<NumberType, number_integer_t>::value or
- std::is_same<NumberType, binary_char_t>::value,
- int> = 0>
+ template < typename NumberType, detail::enable_if_t <
+ 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
@@ -15602,12 +16647,12 @@ class serializer
}
// use a pointer to fill the buffer
- auto buffer_ptr = number_buffer.begin();
+ auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
- const bool is_negative = std::is_same<NumberType, number_integer_t>::value and not(x >= 0); // see issue #755
+ const bool is_negative = std::is_same<NumberType, number_integer_t>::value && !(x >= 0); // see issue #755
number_unsigned_t abs_value;
- unsigned int n_chars;
+ unsigned int n_chars{};
if (is_negative)
{
@@ -15624,7 +16669,7 @@ class serializer
}
// spare 1 byte for '\0'
- assert(n_chars < number_buffer.size() - 1);
+ 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
@@ -15665,7 +16710,7 @@ class serializer
void dump_float(number_float_t x)
{
// NaN / inf
- if (not std::isfinite(x))
+ if (!std::isfinite(x))
{
o->write_characters("null", 4);
return;
@@ -15677,16 +16722,16 @@ class serializer
//
// 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 and std::numeric_limits<number_float_t>::digits == 24 and std::numeric_limits<number_float_t>::max_exponent == 128) or
- (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 53 and std::numeric_limits<number_float_t>::max_exponent == 1024);
+ = (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*/)
{
- char* begin = number_buffer.data();
- char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
+ 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));
}
@@ -15697,27 +16742,28 @@ class serializer
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
- assert(len > 0);
+ JSON_ASSERT(len > 0);
// check if buffer was large enough
- assert(static_cast<std::size_t>(len) < number_buffer.size());
+ JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
// erase thousands separator
if (thousands_sep != '\0')
{
- const auto end = std::remove(number_buffer.begin(),
- number_buffer.begin() + len, thousands_sep);
+ auto* const end = std::remove(number_buffer.begin(),
+ number_buffer.begin() + len, thousands_sep);
std::fill(end, number_buffer.end(), '\0');
- assert((end - number_buffer.begin()) <= len);
+ JSON_ASSERT((end - number_buffer.begin()) <= len);
len = (end - number_buffer.begin());
}
// convert decimal point to '.'
- if (decimal_point != '\0' and decimal_point != '.')
+ if (decimal_point != '\0' && decimal_point != '.')
{
- const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
+ auto* const dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
if (dec_pos != number_buffer.end())
{
*dec_pos = '.';
@@ -15731,7 +16777,7 @@ class serializer
std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
[](char c)
{
- return c == '.' or c == 'e';
+ return c == '.' || c == 'e';
});
if (value_is_int_like)
@@ -15783,6 +16829,7 @@ class serializer
}
};
+ JSON_ASSERT(byte < utf8d.size());
const std::uint8_t type = utf8d[byte];
codep = (state != UTF8_ACCEPT)
@@ -15790,7 +16837,7 @@ class serializer
: (0xFFu >> type) & (byte);
std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
- assert(index < 400);
+ JSON_ASSERT(index < 400);
state = utf8d[index];
return state;
}
@@ -15802,7 +16849,7 @@ class serializer
*/
number_unsigned_t remove_sign(number_unsigned_t x)
{
- assert(false); // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
return x; // LCOV_EXCL_LINE
}
@@ -15817,7 +16864,7 @@ class serializer
*/
inline number_unsigned_t remove_sign(number_integer_t x) noexcept
{
- assert(x < 0 and x < (std::numeric_limits<number_integer_t>::max)());
+ 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;
}
@@ -15853,6 +16900,203 @@ class serializer
// #include <nlohmann/json_fwd.hpp>
+// #include <nlohmann/ordered_map.hpp>
+
+
+#include <functional> // 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>
+
+
+namespace nlohmann
+{
+
+/// 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 typename Container::iterator;
+ using typename Container::const_iterator;
+ using typename Container::size_type;
+ using typename Container::value_type;
+
+ // Explicit constructors instead of `using Container::Container`
+ // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
+ ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {}
+ template <class It>
+ ordered_map(It first, It last, const Allocator& alloc = Allocator())
+ : Container{first, last, alloc} {}
+ ordered_map(std::initializer_list<T> 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 (it->first == key)
+ {
+ return {it, false};
+ }
+ }
+ Container::emplace_back(key, t);
+ return {--this->end(), true};
+ }
+
+ T& operator[](const Key& key)
+ {
+ return emplace(key, T{}).first->second;
+ }
+
+ const T& operator[](const Key& key) const
+ {
+ return at(key);
+ }
+
+ T& at(const Key& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ const T& at(const Key& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ size_type erase(const Key& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (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)
+ {
+ auto it = pos;
+
+ // 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 pos;
+ }
+
+ size_type count(const Key& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ iterator find(const Key& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (it->first == key)
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ const_iterator find(const Key& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (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 (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);
+ }
+ }
+};
+
+} // namespace nlohmann
+
+
+#if defined(JSON_HAS_CPP_17)
+ #include <string_view>
+#endif
/*!
@brief namespace for Niels Lohmann
@@ -15939,15 +17183,15 @@ The invariants are checked by member function assert_invariant().
@note ObjectType trick from https://stackoverflow.com/a/9860911
@endinternal
-@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange
-Format](http://rfc7159.net/rfc7159)
+@see [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange
+Format](https://tools.ietf.org/html/rfc8259)
@since version 1.0.0
@nosubgrouping
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
-class basic_json
+class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
{
private:
template<detail::value_t> friend struct detail::external_constructor;
@@ -15966,10 +17210,12 @@ class basic_json
friend class ::nlohmann::detail::json_sax_dom_parser;
template<typename BasicJsonType>
friend class ::nlohmann::detail::json_sax_dom_callback_parser;
+ friend class ::nlohmann::detail::exception;
/// workaround type for MSVC
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
+ JSON_PRIVATE_UNLESS_TESTED:
// convenience aliases for types residing in namespace detail;
using lexer = ::nlohmann::detail::lexer_base<basic_json>;
@@ -15977,12 +17223,15 @@ class basic_json
static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(
InputAdapterType adapter,
detail::parser_callback_t<basic_json>cb = nullptr,
- bool allow_exceptions = true
- )
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false
+ )
{
- return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter), std::move(cb), allow_exceptions);
+ return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
+ std::move(cb), allow_exceptions, ignore_comments);
}
+ private:
using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
template<typename BasicJsonType>
using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
@@ -15999,6 +17248,7 @@ class basic_json
using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;
template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
+ JSON_PRIVATE_UNLESS_TESTED:
using serializer = ::nlohmann::detail::serializer<basic_json>;
public:
@@ -16009,6 +17259,8 @@ class basic_json
using json_serializer = JSONSerializer<T, SFINAE>;
/// how to treat decoding errors
using error_handler_t = detail::error_handler_t;
+ /// how to treat CBOR tags
+ using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// helper type for initializer lists of basic_json values
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
@@ -16121,7 +17373,7 @@ class basic_json
{
basic_json result;
- result["copyright"] = "(C) 2013-2017 Niels Lohmann";
+ result["copyright"] = "(C) 2013-2021 Niels Lohmann";
result["name"] = "JSON for Modern C++";
result["url"] = "https://github.com/nlohmann/json";
result["version"]["string"] =
@@ -16193,7 +17445,7 @@ class basic_json
/*!
@brief a type for an object
- [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows:
> An object is an unordered collection of zero or more name/value pairs,
> where a name is a string and a value is a string, number, boolean, null,
> object, or array.
@@ -16247,7 +17499,7 @@ class basic_json
#### Limits
- [RFC 7159](http://rfc7159.net/rfc7159) specifies:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:
> An implementation may set limits on the maximum depth of nesting.
In this class, the object's limit of nesting is not explicitly constrained.
@@ -16261,7 +17513,7 @@ class basic_json
access to object values, a pointer of type `object_t*` must be
dereferenced.
- @sa @ref array_t -- type for an array value
+ @sa see @ref array_t -- type for an array value
@since version 1.0.0
@@ -16270,7 +17522,7 @@ class basic_json
name/value pairs in a different order than they were originally stored. In
fact, keys will be traversed in alphabetical order as `std::map` with
`std::less` is used by default. Please note this behavior conforms to [RFC
- 7159](http://rfc7159.net/rfc7159), because any order implements the
+ 8259](https://tools.ietf.org/html/rfc8259), because any order implements the
specified "unordered" nature of JSON objects.
*/
using object_t = ObjectType<StringType,
@@ -16282,7 +17534,7 @@ class basic_json
/*!
@brief a type for an array
- [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON arrays as follows:
> An array is an ordered sequence of zero or more values.
To store objects in C++, a type is defined by the template parameters
@@ -16306,7 +17558,7 @@ class basic_json
#### Limits
- [RFC 7159](http://rfc7159.net/rfc7159) specifies:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:
> An implementation may set limits on the maximum depth of nesting.
In this class, the array's limit of nesting is not explicitly constrained.
@@ -16319,7 +17571,7 @@ class basic_json
Arrays are stored as pointers in a @ref basic_json type. That is, for any
access to array values, a pointer of type `array_t*` must be dereferenced.
- @sa @ref object_t -- type for an object value
+ @sa see @ref object_t -- type for an object value
@since version 1.0.0
*/
@@ -16328,7 +17580,7 @@ class basic_json
/*!
@brief a type for a string
- [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows:
> A string is a sequence of zero or more Unicode characters.
To store objects in C++, a type is defined by the template parameter
@@ -16355,7 +17607,7 @@ class basic_json
#### String comparison
- [RFC 7159](http://rfc7159.net/rfc7159) states:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) states:
> Software implementations are typically required to test names of object
> members for equality. Implementations that transform the textual
> representation into sequences of Unicode code units and then perform the
@@ -16381,7 +17633,7 @@ class basic_json
/*!
@brief a type for a boolean
- [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a
type which differentiates the two literals `true` and `false`.
To store objects in C++, a type is defined by the template parameter @a
@@ -16407,7 +17659,7 @@ class basic_json
/*!
@brief a type for a number (integer)
- [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:
> The representation of numbers is similar to that used in most
> programming languages. A number is represented in base 10 using decimal
> digits. It contains an integer component that may be prefixed with an
@@ -16445,7 +17697,7 @@ class basic_json
#### Limits
- [RFC 7159](http://rfc7159.net/rfc7159) specifies:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:
> An implementation may set limits on the range and precision of numbers.
When the default type is used, the maximal integer number that can be
@@ -16456,7 +17708,7 @@ class basic_json
will be automatically be stored as @ref number_unsigned_t or @ref
number_float_t.
- [RFC 7159](http://rfc7159.net/rfc7159) further states:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
> Note that when such software is used, numbers that are integers and are
> in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense
> that implementations will agree exactly on their numeric values.
@@ -16468,9 +17720,9 @@ class basic_json
Integer number values are stored directly inside a @ref basic_json type.
- @sa @ref number_float_t -- type for number values (floating-point)
+ @sa see @ref number_float_t -- type for number values (floating-point)
- @sa @ref number_unsigned_t -- type for number values (unsigned integer)
+ @sa see @ref number_unsigned_t -- type for number values (unsigned integer)
@since version 1.0.0
*/
@@ -16479,7 +17731,7 @@ class basic_json
/*!
@brief a type for a number (unsigned)
- [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:
> The representation of numbers is similar to that used in most
> programming languages. A number is represented in base 10 using decimal
> digits. It contains an integer component that may be prefixed with an
@@ -16517,7 +17769,7 @@ class basic_json
#### Limits
- [RFC 7159](http://rfc7159.net/rfc7159) specifies:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:
> An implementation may set limits on the range and precision of numbers.
When the default type is used, the maximal integer number that can be
@@ -16527,7 +17779,7 @@ class basic_json
deserialization, too large or small integer numbers will be automatically
be stored as @ref number_integer_t or @ref number_float_t.
- [RFC 7159](http://rfc7159.net/rfc7159) further states:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) further states:
> Note that when such software is used, numbers that are integers and are
> in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense
> that implementations will agree exactly on their numeric values.
@@ -16540,8 +17792,8 @@ class basic_json
Integer number values are stored directly inside a @ref basic_json type.
- @sa @ref number_float_t -- type for number values (floating-point)
- @sa @ref number_integer_t -- type for number values (integer)
+ @sa see @ref number_float_t -- type for number values (floating-point)
+ @sa see @ref number_integer_t -- type for number values (integer)
@since version 2.0.0
*/
@@ -16550,7 +17802,7 @@ class basic_json
/*!
@brief a type for a number (floating-point)
- [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:
> The representation of numbers is similar to that used in most
> programming languages. A number is represented in base 10 using decimal
> digits. It contains an integer component that may be prefixed with an
@@ -16588,7 +17840,7 @@ class basic_json
#### Limits
- [RFC 7159](http://rfc7159.net/rfc7159) states:
+ [RFC 8259](https://tools.ietf.org/html/rfc8259) states:
> This specification allows implementations to set limits on the range and
> precision of numbers accepted. Since software that implements IEEE
> 754-2008 binary64 (double precision) numbers is generally available and
@@ -16607,9 +17859,9 @@ class basic_json
Floating-point number values are stored directly inside a @ref basic_json
type.
- @sa @ref number_integer_t -- type for number values (integer)
+ @sa see @ref number_integer_t -- type for number values (integer)
- @sa @ref number_unsigned_t -- type for number values (unsigned integer)
+ @sa see @ref number_unsigned_t -- type for number values (unsigned integer)
@since version 1.0.0
*/
@@ -16680,7 +17932,7 @@ class basic_json
- If a subtype is given, it is used and added as unsigned 8-bit integer.
- If no subtype is given, the generic binary subtype 0x00 is used.
- @sa @ref binary -- create a binary array
+ @sa see @ref binary -- create a binary array
@since version 3.8.0
*/
@@ -16697,20 +17949,21 @@ class basic_json
AllocatorType<T> alloc;
using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;
- auto deleter = [&](T * object)
+ auto deleter = [&](T * obj)
{
- AllocatorTraits::deallocate(alloc, object, 1);
+ AllocatorTraits::deallocate(alloc, obj, 1);
};
- std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);
- AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);
- assert(object != nullptr);
- return object.release();
+ std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);
+ AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);
+ JSON_ASSERT(obj != nullptr);
+ return obj.release();
}
////////////////////////
// JSON value storage //
////////////////////////
+ JSON_PRIVATE_UNLESS_TESTED:
/*!
@brief a JSON value
@@ -16829,7 +18082,7 @@ class basic_json
object = nullptr; // silence warning, see #821
if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
{
- JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.8.0")); // LCOV_EXCL_LINE
+ JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1", basic_json())); // LCOV_EXCL_LINE
}
break;
}
@@ -16916,7 +18169,7 @@ class basic_json
}
}
- while (not stack.empty())
+ while (!stack.empty())
{
// move the last item to local variable to be processed
basic_json current_item(std::move(stack.back()));
@@ -16987,6 +18240,7 @@ class basic_json
}
};
+ private:
/*!
@brief checks the class invariants
@@ -16995,13 +18249,87 @@ class basic_json
invariant. Furthermore, it has to be called each time the type of a JSON
value is changed, because the invariant expresses a relationship between
@a m_type and @a m_value.
+
+ Furthermore, the parent relation is checked for arrays and objects: If
+ @a check_parents true and the value is an array or object, then the
+ container's elements must have the current value as parent.
+
+ @param[in] check_parents whether the parent relation should be checked.
+ The value is true by default and should only be set to false
+ during destruction of objects when the invariant does not
+ need to hold.
*/
- void assert_invariant() const noexcept
+ void assert_invariant(bool check_parents = true) const noexcept
+ {
+ JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);
+ JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);
+ JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);
+ JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);
+
+#if JSON_DIAGNOSTICS
+ JSON_TRY
+ {
+ // cppcheck-suppress assertWithSideEffect
+ JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)
+ {
+ return j.m_parent == this;
+ }));
+ }
+ JSON_CATCH(...) {} // LCOV_EXCL_LINE
+#endif
+ static_cast<void>(check_parents);
+ }
+
+ void set_parents()
+ {
+#if JSON_DIAGNOSTICS
+ switch (m_type)
+ {
+ case value_t::array:
+ {
+ for (auto& element : *m_value.array)
+ {
+ element.m_parent = this;
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ for (auto& element : *m_value.object)
+ {
+ element.second.m_parent = this;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+#endif
+ }
+
+ iterator set_parents(iterator it, typename iterator::difference_type count)
+ {
+#if JSON_DIAGNOSTICS
+ for (typename iterator::difference_type i = 0; i < count; ++i)
+ {
+ (it + i)->m_parent = this;
+ }
+#else
+ static_cast<void>(count);
+#endif
+ return it;
+ }
+
+ reference set_parent(reference j)
{
- assert(m_type != value_t::object or m_value.object != nullptr);
- assert(m_type != value_t::array or m_value.array != nullptr);
- assert(m_type != value_t::string or m_value.string != nullptr);
- assert(m_type != value_t::binary or m_value.binary != nullptr);
+#if JSON_DIAGNOSTICS
+ j.m_parent = this;
+#else
+ static_cast<void>(j);
+#endif
+ return j;
}
public:
@@ -17022,7 +18350,7 @@ class basic_json
@image html callback_events.png "Example when certain parse events are triggered"
- @sa @ref parser_callback_t for more information and examples
+ @sa see @ref parser_callback_t for more information and examples
*/
using parse_event_t = detail::parse_event_t;
@@ -17071,7 +18399,7 @@ class basic_json
should be kept (`true`) or not (`false`). In the latter case, it is either
skipped completely or replaced by an empty discarded object.
- @sa @ref parse for examples
+ @sa see @ref parse for examples
@since version 1.0.0
*/
@@ -17112,7 +18440,7 @@ class basic_json
@liveexample{The following code shows the constructor for different @ref
value_t values,basic_json__value_t}
- @sa @ref clear() -- restores the postcondition of this constructor
+ @sa see @ref clear() -- restores the postcondition of this constructor
@since version 1.0.0
*/
@@ -17188,8 +18516,7 @@ class basic_json
- @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)
- @a CompatibleType is not a @ref basic_json nested type (e.g.,
@ref json_pointer, @ref iterator, etc ...)
- - @ref @ref json_serializer<U> has a
- `to_json(basic_json_t&, CompatibleType&&)` method
+ - `json_serializer<U>` has a `to_json(basic_json_t&, CompatibleType&&)` method
@tparam U = `uncvref_t<CompatibleType>`
@@ -17209,15 +18536,16 @@ class basic_json
@since version 2.1.0
*/
- template <typename CompatibleType,
- typename U = detail::uncvref_t<CompatibleType>,
- detail::enable_if_t<
- not detail::is_basic_json<U>::value and detail::is_compatible_type<basic_json_t, U>::value, int> = 0>
- basic_json(CompatibleType && val) noexcept(noexcept(
+ template < typename CompatibleType,
+ typename U = detail::uncvref_t<CompatibleType>,
+ detail::enable_if_t <
+ !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >
+ basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
std::forward<CompatibleType>(val))))
{
JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
+ set_parents();
assert_invariant();
}
@@ -17247,9 +18575,9 @@ class basic_json
@since version 3.2.0
*/
- template <typename BasicJsonType,
- detail::enable_if_t<
- detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0>
+ template < typename BasicJsonType,
+ detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >
basic_json(const BasicJsonType& val)
{
using other_boolean_t = typename BasicJsonType::boolean_t;
@@ -17294,8 +18622,9 @@ class basic_json
m_type = value_t::discarded;
break;
default: // LCOV_EXCL_LINE
- assert(false); // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
+ set_parents();
assert_invariant();
}
@@ -17366,9 +18695,9 @@ class basic_json
@liveexample{The example below shows how JSON values are created from
initializer lists.,basic_json__list_init_t}
- @sa @ref array(initializer_list_t) -- create a JSON array
+ @sa see @ref array(initializer_list_t) -- create a JSON array
value from an initializer list
- @sa @ref object(initializer_list_t) -- create a JSON object
+ @sa see @ref object(initializer_list_t) -- create a JSON object
value from an initializer list
@since version 1.0.0
@@ -17382,11 +18711,11 @@ class basic_json
bool is_an_object = std::all_of(init.begin(), init.end(),
[](const detail::json_ref<basic_json>& element_ref)
{
- return element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string();
+ return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();
});
// adjust type if type deduction is not wanted
- if (not type_deduction)
+ if (!type_deduction)
{
// if array is wanted, do not create an object though possible
if (manual_type == value_t::array)
@@ -17395,9 +18724,9 @@ class basic_json
}
// if object is wanted but impossible, throw an exception
- if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object and not is_an_object))
+ if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
{
- JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
+ JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json()));
}
}
@@ -17407,13 +18736,13 @@ class basic_json
m_type = value_t::object;
m_value = value_t::object;
- std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref)
+ for (auto& element_ref : init)
{
auto element = element_ref.moved_or_copied();
m_value.object->emplace(
std::move(*((*element.m_value.array)[0].m_value.string)),
std::move((*element.m_value.array)[1]));
- });
+ }
}
else
{
@@ -17422,6 +18751,7 @@ class basic_json
m_value.array = create<array_t>(init.begin(), init.end());
}
+ set_parents();
assert_invariant();
}
@@ -17548,9 +18878,9 @@ class basic_json
@liveexample{The following code shows an example for the `array`
function.,array}
- @sa @ref basic_json(initializer_list_t, bool, value_t) --
+ @sa see @ref basic_json(initializer_list_t, bool, value_t) --
create a JSON value from an initializer list
- @sa @ref object(initializer_list_t) -- create a JSON object
+ @sa see @ref object(initializer_list_t) -- create a JSON object
value from an initializer list
@since version 1.0.0
@@ -17592,9 +18922,9 @@ class basic_json
@liveexample{The following code shows an example for the `object`
function.,object}
- @sa @ref basic_json(initializer_list_t, bool, value_t) --
+ @sa see @ref basic_json(initializer_list_t, bool, value_t) --
create a JSON value from an initializer list
- @sa @ref array(initializer_list_t) -- create a JSON array
+ @sa see @ref array(initializer_list_t) -- create a JSON array
value from an initializer list
@since version 1.0.0
@@ -17631,6 +18961,7 @@ class basic_json
: m_type(value_t::array)
{
m_value.array = create<array_t>(cnt, val);
+ set_parents();
assert_invariant();
}
@@ -17689,18 +19020,18 @@ class basic_json
@since version 1.0.0
*/
- template<class InputIT, typename std::enable_if<
- std::is_same<InputIT, typename basic_json_t::iterator>::value or
- std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>
+ template < class InputIT, typename std::enable_if <
+ std::is_same<InputIT, typename basic_json_t::iterator>::value ||
+ std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >
basic_json(InputIT first, InputIT last)
{
- assert(first.m_object != nullptr);
- assert(last.m_object != nullptr);
+ JSON_ASSERT(first.m_object != nullptr);
+ JSON_ASSERT(last.m_object != nullptr);
// make sure iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
- JSON_THROW(invalid_iterator::create(201, "iterators are not compatible"));
+ JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json()));
}
// copy type from first iterator
@@ -17715,10 +19046,10 @@ class basic_json
case value_t::number_unsigned:
case value_t::string:
{
- if (JSON_HEDLEY_UNLIKELY(not first.m_it.primitive_iterator.is_begin()
- or not last.m_it.primitive_iterator.is_end()))
+ if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
+ || !last.m_it.primitive_iterator.is_end()))
{
- JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object));
}
break;
}
@@ -17780,10 +19111,10 @@ class basic_json
}
default:
- JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " +
- std::string(first.m_object->type_name())));
+ JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object));
}
+ set_parents();
assert_invariant();
}
@@ -17792,9 +19123,9 @@ class basic_json
// other constructors and destructor //
///////////////////////////////////////
- template <typename JsonRef,
- detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
- std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
+ template<typename JsonRef,
+ detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
+ std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}
/*!
@@ -17882,6 +19213,7 @@ class basic_json
break;
}
+ set_parents();
assert_invariant();
}
@@ -17916,12 +19248,13 @@ class basic_json
m_value(std::move(other.m_value))
{
// check that passed value is valid
- other.assert_invariant();
+ other.assert_invariant(false);
// invalidate payload
other.m_type = value_t::null;
other.m_value = {};
+ set_parents();
assert_invariant();
}
@@ -17949,9 +19282,9 @@ class basic_json
@since version 1.0.0
*/
basic_json& operator=(basic_json other) noexcept (
- std::is_nothrow_move_constructible<value_t>::value and
- std::is_nothrow_move_assignable<value_t>::value and
- std::is_nothrow_move_constructible<json_value>::value and
+ std::is_nothrow_move_constructible<value_t>::value&&
+ std::is_nothrow_move_assignable<value_t>::value&&
+ std::is_nothrow_move_constructible<json_value>::value&&
std::is_nothrow_move_assignable<json_value>::value
)
{
@@ -17962,6 +19295,7 @@ class basic_json
swap(m_type, other.m_type);
swap(m_value, other.m_value);
+ set_parents();
assert_invariant();
return *this;
}
@@ -17983,7 +19317,7 @@ class basic_json
*/
~basic_json() noexcept
{
- assert_invariant();
+ assert_invariant(false);
m_value.destroy(m_type);
}
@@ -18017,7 +19351,8 @@ class basic_json
@param[in] error_handler how to react on decoding errors; there are three
possible values: `strict` (throws and exception in case a decoding error
occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),
- and `ignore` (ignore invalid UTF-8 sequences during serialization).
+ and `ignore` (ignore invalid UTF-8 sequences during serialization; all
+ bytes are copied to the output unchanged).
@return string containing the serialization of the JSON value
@@ -18092,8 +19427,8 @@ class basic_json
@liveexample{The following code exemplifies `type()` for all JSON
types.,type}
- @sa @ref operator value_t() -- return the type of the JSON value (implicit)
- @sa @ref type_name() -- return the type as string
+ @sa see @ref operator value_t() -- return the type of the JSON value (implicit)
+ @sa see @ref type_name() -- return the type as string
@since version 1.0.0
*/
@@ -18119,18 +19454,18 @@ class basic_json
@liveexample{The following code exemplifies `is_primitive()` for all JSON
types.,is_primitive}
- @sa @ref is_structured() -- returns whether JSON value is structured
- @sa @ref is_null() -- returns whether JSON value is `null`
- @sa @ref is_string() -- returns whether JSON value is a string
- @sa @ref is_boolean() -- returns whether JSON value is a boolean
- @sa @ref is_number() -- returns whether JSON value is a number
- @sa @ref is_binary() -- returns whether JSON value is a binary array
+ @sa see @ref is_structured() -- returns whether JSON value is structured
+ @sa see @ref is_null() -- returns whether JSON value is `null`
+ @sa see @ref is_string() -- returns whether JSON value is a string
+ @sa see @ref is_boolean() -- returns whether JSON value is a boolean
+ @sa see @ref is_number() -- returns whether JSON value is a number
+ @sa see @ref is_binary() -- returns whether JSON value is a binary array
@since version 1.0.0
*/
constexpr bool is_primitive() const noexcept
{
- return is_null() or is_string() or is_boolean() or is_number() or is_binary();
+ return is_null() || is_string() || is_boolean() || is_number() || is_binary();
}
/*!
@@ -18149,15 +19484,15 @@ class basic_json
@liveexample{The following code exemplifies `is_structured()` for all JSON
types.,is_structured}
- @sa @ref is_primitive() -- returns whether value is primitive
- @sa @ref is_array() -- returns whether value is an array
- @sa @ref is_object() -- returns whether value is an object
+ @sa see @ref is_primitive() -- returns whether value is primitive
+ @sa see @ref is_array() -- returns whether value is an array
+ @sa see @ref is_object() -- returns whether value is an object
@since version 1.0.0
*/
constexpr bool is_structured() const noexcept
{
- return is_array() or is_object();
+ return is_array() || is_object();
}
/*!
@@ -18221,17 +19556,17 @@ class basic_json
@liveexample{The following code exemplifies `is_number()` for all JSON
types.,is_number}
- @sa @ref is_number_integer() -- check if value is an integer or unsigned
+ @sa see @ref is_number_integer() -- check if value is an integer or unsigned
integer number
- @sa @ref is_number_unsigned() -- check if value is an unsigned integer
+ @sa see @ref is_number_unsigned() -- check if value is an unsigned integer
number
- @sa @ref is_number_float() -- check if value is a floating-point number
+ @sa see @ref is_number_float() -- check if value is a floating-point number
@since version 1.0.0
*/
constexpr bool is_number() const noexcept
{
- return is_number_integer() or is_number_float();
+ return is_number_integer() || is_number_float();
}
/*!
@@ -18251,16 +19586,16 @@ class basic_json
@liveexample{The following code exemplifies `is_number_integer()` for all
JSON types.,is_number_integer}
- @sa @ref is_number() -- check if value is a number
- @sa @ref is_number_unsigned() -- check if value is an unsigned integer
+ @sa see @ref is_number() -- check if value is a number
+ @sa see @ref is_number_unsigned() -- check if value is an unsigned integer
number
- @sa @ref is_number_float() -- check if value is a floating-point number
+ @sa see @ref is_number_float() -- check if value is a floating-point number
@since version 1.0.0
*/
constexpr bool is_number_integer() const noexcept
{
- return m_type == value_t::number_integer or m_type == value_t::number_unsigned;
+ return m_type == value_t::number_integer || m_type == value_t::number_unsigned;
}
/*!
@@ -18279,10 +19614,10 @@ class basic_json
@liveexample{The following code exemplifies `is_number_unsigned()` for all
JSON types.,is_number_unsigned}
- @sa @ref is_number() -- check if value is a number
- @sa @ref is_number_integer() -- check if value is an integer or unsigned
+ @sa see @ref is_number() -- check if value is a number
+ @sa see @ref is_number_integer() -- check if value is an integer or unsigned
integer number
- @sa @ref is_number_float() -- check if value is a floating-point number
+ @sa see @ref is_number_float() -- check if value is a floating-point number
@since version 2.0.0
*/
@@ -18307,9 +19642,9 @@ class basic_json
@liveexample{The following code exemplifies `is_number_float()` for all
JSON types.,is_number_float}
- @sa @ref is_number() -- check if value is number
- @sa @ref is_number_integer() -- check if value is an integer number
- @sa @ref is_number_unsigned() -- check if value is an unsigned integer
+ @sa see @ref is_number() -- check if value is number
+ @sa see @ref is_number_integer() -- check if value is an integer number
+ @sa see @ref is_number_unsigned() -- check if value is an unsigned integer
number
@since version 1.0.0
@@ -18450,8 +19785,8 @@ class basic_json
@liveexample{The following code exemplifies the @ref value_t operator for
all JSON types.,operator__value_t}
- @sa @ref type() -- return the type of the JSON value (explicit)
- @sa @ref type_name() -- return the type as string
+ @sa see @ref type() -- return the type of the JSON value (explicit)
+ @sa see @ref type_name() -- return the type as string
@since version 1.0.0
*/
@@ -18475,7 +19810,7 @@ class basic_json
return m_value.boolean;
}
- JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name())));
+ JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this));
}
/// get a pointer to the value (object)
@@ -18589,14 +19924,14 @@ class basic_json
static ReferenceType get_ref_impl(ThisType& obj)
{
// delegate the call to get_ptr<>()
- auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
+ auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
if (JSON_HEDLEY_LIKELY(ptr != nullptr))
{
return *ptr;
}
- JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name())));
+ JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj));
}
public:
@@ -18605,50 +19940,53 @@ class basic_json
/// @{
/*!
- @brief get special-case overload
+ @brief get a pointer value (implicit)
- This overloads avoids a lot of template boilerplate, it can be seen as the
- identity method
+ Implicit pointer access to the internally stored JSON value. No copies are
+ made.
- @tparam BasicJsonType == @ref basic_json
+ @warning Writing data to the pointee of the result yields an undefined
+ state.
- @return a copy of *this
+ @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
+ object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
+ @ref number_unsigned_t, or @ref number_float_t. Enforced by a static
+ assertion.
+
+ @return pointer to the internally stored JSON value if the requested
+ pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@complexity Constant.
- @since version 2.1.0
+ @liveexample{The example below shows how pointers to internal values of a
+ JSON value can be requested. Note that no type conversions are made and a
+ `nullptr` is returned if the value and the requested pointer type does not
+ match.,get_ptr}
+
+ @since version 1.0.0
*/
- template<typename BasicJsonType, detail::enable_if_t<
- std::is_same<typename std::remove_const<BasicJsonType>::type, basic_json_t>::value,
- int> = 0>
- basic_json get() const
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
+ auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
{
- return *this;
+ // delegate the call to get_impl_ptr<>()
+ return get_impl_ptr(static_cast<PointerType>(nullptr));
}
/*!
- @brief get special-case overload
-
- This overloads converts the current @ref basic_json in a different
- @ref basic_json type
-
- @tparam BasicJsonType == @ref basic_json
-
- @return a copy of *this, converted into @tparam BasicJsonType
-
- @complexity Depending on the implementation of the called `from_json()`
- method.
-
- @since version 3.2.0
+ @brief get a pointer value (implicit)
+ @copydoc get_ptr()
*/
- template<typename BasicJsonType, detail::enable_if_t<
- not std::is_same<BasicJsonType, basic_json>::value and
- detail::is_basic_json<BasicJsonType>::value, int> = 0>
- BasicJsonType get() const
+ template < typename PointerType, typename std::enable_if <
+ std::is_pointer<PointerType>::value&&
+ std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >
+ constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
{
- return *this;
+ // delegate the call to get_impl_ptr<>() const
+ return get_impl_ptr(static_cast<PointerType>(nullptr));
}
+ private:
/*!
@brief get a value (explicit)
@@ -18672,7 +20010,6 @@ class basic_json
- @ref json_serializer<ValueType> does not have a `from_json()` method of
the form `ValueType from_json(const basic_json&)`
- @tparam ValueTypeCV the provided value type
@tparam ValueType the returned value type
@return copy of the JSON value, converted to @a ValueType
@@ -18688,24 +20025,15 @@ class basic_json
@since version 2.1.0
*/
- template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
- detail::enable_if_t <
- not detail::is_basic_json<ValueType>::value and
- detail::has_from_json<basic_json_t, ValueType>::value and
- not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
- int> = 0>
- ValueType get() const noexcept(noexcept(
- JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
+ template < typename ValueType,
+ detail::enable_if_t <
+ detail::is_default_constructible<ValueType>::value&&
+ detail::has_from_json<basic_json_t, ValueType>::value,
+ int > = 0 >
+ ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
{
- // we cannot static_assert on ValueTypeCV being non-const, because
- // there is support for get<const basic_json_t>(), which is why we
- // still need the uncvref
- static_assert(not std::is_reference<ValueTypeCV>::value,
- "get() cannot be used with reference types, you might want to use get_ref()");
- static_assert(std::is_default_constructible<ValueType>::value,
- "types must be DefaultConstructible when used with get()");
-
- ValueType ret;
+ ValueType ret{};
JSONSerializer<ValueType>::from_json(*this, ret);
return ret;
}
@@ -18721,7 +20049,7 @@ class basic_json
The function is equivalent to executing
@code {.cpp}
- return JSONSerializer<ValueTypeCV>::from_json(*this);
+ return JSONSerializer<ValueType>::from_json(*this);
@endcode
This overloads is chosen if:
@@ -18732,7 +20060,6 @@ class basic_json
@note If @ref json_serializer<ValueType> has both overloads of
`from_json()`, this one is chosen.
- @tparam ValueTypeCV the provided value type
@tparam ValueType the returned value type
@return copy of the JSON value, converted to @a ValueType
@@ -18741,122 +20068,116 @@ class basic_json
@since version 2.1.0
*/
- template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
- detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and
- detail::has_non_default_from_json<basic_json_t, ValueType>::value,
- int> = 0>
- ValueType get() const noexcept(noexcept(
- JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))
+ template < typename ValueType,
+ detail::enable_if_t <
+ detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+ int > = 0 >
+ ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))
{
- static_assert(not std::is_reference<ValueTypeCV>::value,
- "get() cannot be used with reference types, you might want to use get_ref()");
return JSONSerializer<ValueType>::from_json(*this);
}
/*!
- @brief get a value (explicit)
+ @brief get special-case overload
- Explicit type conversion between the JSON value and a compatible value.
- The value is filled into the input parameter by calling the @ref json_serializer<ValueType>
- `from_json()` method.
+ This overloads converts the current @ref basic_json in a different
+ @ref basic_json type
- The function is equivalent to executing
- @code {.cpp}
- ValueType v;
- JSONSerializer<ValueType>::from_json(*this, v);
- @endcode
+ @tparam BasicJsonType == @ref basic_json
- This overloads is chosen if:
- - @a ValueType is not @ref basic_json,
- - @ref json_serializer<ValueType> has a `from_json()` method of the form
- `void from_json(const basic_json&, ValueType&)`, and
+ @return a copy of *this, converted into @a BasicJsonType
- @tparam ValueType the input parameter type.
+ @complexity Depending on the implementation of the called `from_json()`
+ method.
- @return the input parameter, allowing chaining calls.
+ @since version 3.2.0
+ */
+ template < typename BasicJsonType,
+ detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value,
+ int > = 0 >
+ BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
+ {
+ return *this;
+ }
- @throw what @ref json_serializer<ValueType> `from_json()` method throws
+ /*!
+ @brief get special-case overload
- @liveexample{The example below shows several conversions from JSON values
- to other types. There a few things to note: (1) Floating-point numbers can
- be converted to integers\, (2) A JSON array can be converted to a standard
- `std::vector<short>`\, (3) A JSON object can be converted to C++
- associative containers such as `std::unordered_map<std::string\,
- json>`.,get_to}
+ This overloads avoids a lot of template boilerplate, it can be seen as the
+ identity method
- @since version 3.3.0
+ @tparam BasicJsonType == @ref basic_json
+
+ @return a copy of *this
+
+ @complexity Constant.
+
+ @since version 2.1.0
*/
- template<typename ValueType,
- detail::enable_if_t <
- not detail::is_basic_json<ValueType>::value and
- detail::has_from_json<basic_json_t, ValueType>::value,
+ template<typename BasicJsonType,
+ detail::enable_if_t<
+ std::is_same<BasicJsonType, basic_json_t>::value,
int> = 0>
- ValueType & get_to(ValueType& v) const noexcept(noexcept(
- JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
+ basic_json get_impl(detail::priority_tag<3> /*unused*/) const
{
- JSONSerializer<ValueType>::from_json(*this, v);
- return v;
+ return *this;
}
- template <
- typename T, std::size_t N,
- typename Array = T (&)[N],
- detail::enable_if_t <
- detail::has_from_json<basic_json_t, Array>::value, int > = 0 >
- Array get_to(T (&v)[N]) const
- noexcept(noexcept(JSONSerializer<Array>::from_json(
- std::declval<const basic_json_t&>(), v)))
+ /*!
+ @brief get a pointer value (explicit)
+ @copydoc get()
+ */
+ template<typename PointerType,
+ detail::enable_if_t<
+ std::is_pointer<PointerType>::value,
+ int> = 0>
+ constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept
+ -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
{
- JSONSerializer<Array>::from_json(*this, v);
- return v;
+ // delegate the call to get_ptr
+ return get_ptr<PointerType>();
}
-
+ public:
/*!
- @brief get a pointer value (implicit)
+ @brief get a (pointer) value (explicit)
- Implicit pointer access to the internally stored JSON value. No copies are
- made.
+ Performs explicit type conversion between the JSON value and a compatible value if required.
- @warning Writing data to the pointee of the result yields an undefined
- state.
+ - If the requested type is a pointer to the internally stored JSON value that pointer is returned.
+ No copies are made.
- @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
- object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
- @ref number_unsigned_t, or @ref number_float_t. Enforced by a static
- assertion.
+ - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible
+ from the current @ref basic_json.
- @return pointer to the internally stored JSON value if the requested
- pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
+ - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`
+ method.
- @complexity Constant.
+ @tparam ValueTypeCV the provided value type
+ @tparam ValueType the returned value type
- @liveexample{The example below shows how pointers to internal values of a
- JSON value can be requested. Note that no type conversions are made and a
- `nullptr` is returned if the value and the requested pointer type does not
- match.,get_ptr}
+ @return copy of the JSON value, converted to @tparam ValueType if necessary
- @since version 1.0.0
- */
- template<typename PointerType, typename std::enable_if<
- std::is_pointer<PointerType>::value, int>::type = 0>
- auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
- {
- // delegate the call to get_impl_ptr<>()
- return get_impl_ptr(static_cast<PointerType>(nullptr));
- }
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
- /*!
- @brief get a pointer value (implicit)
- @copydoc get_ptr()
+ @since version 2.1.0
*/
- template<typename PointerType, typename std::enable_if<
- std::is_pointer<PointerType>::value and
- std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
- constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
+#if defined(JSON_HAS_CPP_14)
+ constexpr
+#endif
+ auto get() const noexcept(
+ noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))
+ -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))
{
- // delegate the call to get_impl_ptr<>() const
- return get_impl_ptr(static_cast<PointerType>(nullptr));
+ // we cannot static_assert on ValueTypeCV being non-const, because
+ // there is support for get<const basic_json_t>(), which is why we
+ // still need the uncvref
+ static_assert(!std::is_reference<ValueTypeCV>::value,
+ "get() cannot be used with reference types, you might want to use get_ref()");
+ return get_impl<ValueType>(detail::priority_tag<4> {});
}
/*!
@@ -18882,7 +20203,7 @@ class basic_json
`nullptr` is returned if the value and the requested pointer type does not
match.,get__PointerType}
- @sa @ref get_ptr() for explicit pointer-member access
+ @sa see @ref get_ptr() for explicit pointer-member access
@since version 1.0.0
*/
@@ -18895,15 +20216,73 @@ class basic_json
}
/*!
- @brief get a pointer value (explicit)
- @copydoc get()
+ @brief get a value (explicit)
+
+ Explicit type conversion between the JSON value and a compatible value.
+ The value is filled into the input parameter by calling the @ref json_serializer<ValueType>
+ `from_json()` method.
+
+ The function is equivalent to executing
+ @code {.cpp}
+ ValueType v;
+ JSONSerializer<ValueType>::from_json(*this, v);
+ @endcode
+
+ This overloads is chosen if:
+ - @a ValueType is not @ref basic_json,
+ - @ref json_serializer<ValueType> has a `from_json()` method of the form
+ `void from_json(const basic_json&, ValueType&)`, and
+
+ @tparam ValueType the input parameter type.
+
+ @return the input parameter, allowing chaining calls.
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+ @liveexample{The example below shows several conversions from JSON values
+ to other types. There a few things to note: (1) Floating-point numbers can
+ be converted to integers\, (2) A JSON array can be converted to a standard
+ `std::vector<short>`\, (3) A JSON object can be converted to C++
+ associative containers such as `std::unordered_map<std::string\,
+ json>`.,get_to}
+
+ @since version 3.3.0
*/
- template<typename PointerType, typename std::enable_if<
- std::is_pointer<PointerType>::value, int>::type = 0>
- constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
+ template < typename ValueType,
+ detail::enable_if_t <
+ !detail::is_basic_json<ValueType>::value&&
+ detail::has_from_json<basic_json_t, ValueType>::value,
+ int > = 0 >
+ ValueType & get_to(ValueType& v) const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
{
- // delegate the call to get_ptr
- return get_ptr<PointerType>();
+ JSONSerializer<ValueType>::from_json(*this, v);
+ return v;
+ }
+
+ // specialization to allow to call get_to with a basic_json value
+ // see https://github.com/nlohmann/json/issues/2175
+ template<typename ValueType,
+ detail::enable_if_t <
+ detail::is_basic_json<ValueType>::value,
+ int> = 0>
+ ValueType & get_to(ValueType& v) const
+ {
+ v = *this;
+ return v;
+ }
+
+ template <
+ typename T, std::size_t N,
+ typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ detail::enable_if_t <
+ detail::has_from_json<basic_json_t, Array>::value, int > = 0 >
+ Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ noexcept(noexcept(JSONSerializer<Array>::from_json(
+ std::declval<const basic_json_t&>(), v)))
+ {
+ JSONSerializer<Array>::from_json(*this, v);
+ return v;
}
/*!
@@ -18944,9 +20323,9 @@ class basic_json
@brief get a reference value (implicit)
@copydoc get_ref()
*/
- template<typename ReferenceType, typename std::enable_if<
- std::is_reference<ReferenceType>::value and
- std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>
+ template < typename ReferenceType, typename std::enable_if <
+ std::is_reference<ReferenceType>::value&&
+ std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >
ReferenceType get_ref() const
{
// delegate call to get_ref_impl
@@ -18983,17 +20362,17 @@ class basic_json
@since version 1.0.0
*/
template < typename ValueType, typename std::enable_if <
- not std::is_pointer<ValueType>::value and
- not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
- not std::is_same<ValueType, typename string_t::value_type>::value and
- not detail::is_basic_json<ValueType>::value
- and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
-#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) and _MSC_VER >= 1910 and _MSC_VER <= 1914))
- and not std::is_same<ValueType, typename std::string_view>::value
-#endif
- and detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
+ !std::is_pointer<ValueType>::value&&
+ !std::is_same<ValueType, detail::json_ref<basic_json>>::value&&
+ !std::is_same<ValueType, typename string_t::value_type>::value&&
+ !detail::is_basic_json<ValueType>::value
+ && !std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
+#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
+ && !std::is_same<ValueType, typename std::string_view>::value
+#endif
+ && detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
, int >::type = 0 >
- operator ValueType() const
+ JSON_EXPLICIT operator ValueType() const
{
// delegate the call to get<>() const
return get<ValueType>();
@@ -19004,15 +20383,15 @@ class basic_json
@throw type_error.302 if the value is not binary
- @sa @ref is_binary() to check if the value is binary
+ @sa see @ref is_binary() to check if the value is binary
@since version 3.8.0
*/
binary_t& get_binary()
{
- if (not is_binary())
+ if (!is_binary())
{
- JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name())));
+ JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this));
}
return *get_ptr<binary_t*>();
@@ -19021,9 +20400,9 @@ class basic_json
/// @copydoc get_binary()
const binary_t& get_binary() const
{
- if (not is_binary())
+ if (!is_binary())
{
- JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name())));
+ JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this));
}
return *get_ptr<const binary_t*>();
@@ -19073,17 +20452,17 @@ class basic_json
{
JSON_TRY
{
- return m_value.array->at(idx);
+ return set_parent(m_value.array->at(idx));
}
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this));
}
}
else
{
- JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
}
}
@@ -19125,12 +20504,12 @@ class basic_json
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this));
}
}
else
{
- JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
}
}
@@ -19154,9 +20533,9 @@ class basic_json
@complexity Logarithmic in the size of the container.
- @sa @ref operator[](const typename object_t::key_type&) for unchecked
+ @sa see @ref operator[](const typename object_t::key_type&) for unchecked
access by reference
- @sa @ref value() for access by value with a default value
+ @sa see @ref value() for access by value with a default value
@since version 1.0.0
@@ -19171,17 +20550,17 @@ class basic_json
{
JSON_TRY
{
- return m_value.object->at(key);
+ return set_parent(m_value.object->at(key));
}
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
+ JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this));
}
}
else
{
- JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
}
}
@@ -19205,9 +20584,9 @@ class basic_json
@complexity Logarithmic in the size of the container.
- @sa @ref operator[](const typename object_t::key_type&) for unchecked
+ @sa see @ref operator[](const typename object_t::key_type&) for unchecked
access by reference
- @sa @ref value() for access by value with a default value
+ @sa see @ref value() for access by value with a default value
@since version 1.0.0
@@ -19227,12 +20606,12 @@ class basic_json
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
+ JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this));
}
}
else
{
- JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
}
}
@@ -19277,15 +20656,22 @@ class basic_json
// fill up array with null values if given idx is outside range
if (idx >= m_value.array->size())
{
- m_value.array->insert(m_value.array->end(),
- idx - m_value.array->size() + 1,
- basic_json());
+#if JSON_DIAGNOSTICS
+ // remember array size before resizing
+ const auto previous_size = m_value.array->size();
+#endif
+ m_value.array->resize(idx + 1);
+
+#if JSON_DIAGNOSTICS
+ // set parent for values added above
+ set_parents(begin() + static_cast<typename iterator::difference_type>(previous_size), static_cast<typename iterator::difference_type>(idx + 1 - previous_size));
+#endif
}
return m_value.array->operator[](idx);
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this));
}
/*!
@@ -19315,7 +20701,7 @@ class basic_json
return m_value.array->operator[](idx);
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this));
}
/*!
@@ -19339,9 +20725,9 @@ class basic_json
@liveexample{The example below shows how object elements can be read and
written using the `[]` operator.,operatorarray__key_type}
- @sa @ref at(const typename object_t::key_type&) for access by reference
+ @sa see @ref at(const typename object_t::key_type&) for access by reference
with range checking
- @sa @ref value() for access by value with a default value
+ @sa see @ref value() for access by value with a default value
@since version 1.0.0
*/
@@ -19358,10 +20744,10 @@ class basic_json
// operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
- return m_value.object->operator[](key);
+ return set_parent(m_value.object->operator[](key));
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
}
/*!
@@ -19388,9 +20774,9 @@ class basic_json
@liveexample{The example below shows how object elements can be read using
the `[]` operator.,operatorarray__key_type_const}
- @sa @ref at(const typename object_t::key_type&) for access by reference
+ @sa see @ref at(const typename object_t::key_type&) for access by reference
with range checking
- @sa @ref value() for access by value with a default value
+ @sa see @ref value() for access by value with a default value
@since version 1.0.0
*/
@@ -19399,11 +20785,11 @@ class basic_json
// const operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
- assert(m_value.object->find(key) != m_value.object->end());
+ JSON_ASSERT(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
}
/*!
@@ -19427,9 +20813,9 @@ class basic_json
@liveexample{The example below shows how object elements can be read and
written using the `[]` operator.,operatorarray__key_type}
- @sa @ref at(const typename object_t::key_type&) for access by reference
+ @sa see @ref at(const typename object_t::key_type&) for access by reference
with range checking
- @sa @ref value() for access by value with a default value
+ @sa see @ref value() for access by value with a default value
@since version 1.1.0
*/
@@ -19448,10 +20834,10 @@ class basic_json
// at only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
- return m_value.object->operator[](key);
+ return set_parent(m_value.object->operator[](key));
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
}
/*!
@@ -19478,9 +20864,9 @@ class basic_json
@liveexample{The example below shows how object elements can be read using
the `[]` operator.,operatorarray__key_type_const}
- @sa @ref at(const typename object_t::key_type&) for access by reference
+ @sa see @ref at(const typename object_t::key_type&) for access by reference
with range checking
- @sa @ref value() for access by value with a default value
+ @sa see @ref value() for access by value with a default value
@since version 1.1.0
*/
@@ -19491,11 +20877,11 @@ class basic_json
// at only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
- assert(m_value.object->find(key) != m_value.object->end());
+ JSON_ASSERT(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
- JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
}
/*!
@@ -19541,16 +20927,17 @@ class basic_json
@liveexample{The example below shows how object elements can be queried
with a default value.,basic_json__value}
- @sa @ref at(const typename object_t::key_type&) for access by reference
+ @sa see @ref at(const typename object_t::key_type&) for access by reference
with range checking
- @sa @ref operator[](const typename object_t::key_type&) for unchecked
+ @sa see @ref operator[](const typename object_t::key_type&) for unchecked
access by reference
@since version 1.0.0
*/
- template<class ValueType, typename std::enable_if<
- std::is_convertible<basic_json_t, ValueType>::value
- and not std::is_same<value_t, ValueType>::value, int>::type = 0>
+ // using std::is_convertible in a std::enable_if will fail when using explicit conversions
+ template < class ValueType, typename std::enable_if <
+ detail::is_getable<basic_json_t, ValueType>::value
+ && !std::is_same<value_t, ValueType>::value, int >::type = 0 >
ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
{
// at only works for objects
@@ -19560,13 +20947,13 @@ class basic_json
const auto it = find(key);
if (it != end())
{
- return *it;
+ return it->template get<ValueType>();
}
return default_value;
}
- JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this));
}
/*!
@@ -19617,12 +21004,12 @@ class basic_json
@liveexample{The example below shows how object elements can be queried
with a default value.,basic_json__value_ptr}
- @sa @ref operator[](const json_pointer&) for unchecked access by reference
+ @sa see @ref operator[](const json_pointer&) for unchecked access by reference
@since version 2.0.2
*/
template<class ValueType, typename std::enable_if<
- std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
+ detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>
ValueType value(const json_pointer& ptr, const ValueType& default_value) const
{
// at only works for objects
@@ -19631,7 +21018,7 @@ class basic_json
// if pointer resolves a value, return it or use default value
JSON_TRY
{
- return ptr.get_checked(this);
+ return ptr.get_checked(this).template get<ValueType>();
}
JSON_INTERNAL_CATCH (out_of_range&)
{
@@ -19639,7 +21026,7 @@ class basic_json
}
}
- JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this));
}
/*!
@@ -19673,7 +21060,7 @@ class basic_json
@liveexample{The following code shows an example for `front()`.,front}
- @sa @ref back() -- access the last element
+ @sa see @ref back() -- access the last element
@since version 1.0.0
*/
@@ -19717,7 +21104,7 @@ class basic_json
@liveexample{The following code shows an example for `back()`.,back}
- @sa @ref front() -- access the first element
+ @sa see @ref front() -- access the first element
@since version 1.0.0
*/
@@ -19775,25 +21162,25 @@ class basic_json
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType}
- @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
+ @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
- @sa @ref erase(const typename object_t::key_type&) -- removes the element
+ @sa see @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
- @sa @ref erase(const size_type) -- removes the element from an array at
+ @sa see @ref erase(const size_type) -- removes the element from an array at
the given index
@since version 1.0.0
*/
- template<class IteratorType, typename std::enable_if<
- std::is_same<IteratorType, typename basic_json_t::iterator>::value or
- std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
- = 0>
+ template < class IteratorType, typename std::enable_if <
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type
+ = 0 >
IteratorType erase(IteratorType pos)
{
// make sure iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
{
- JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
IteratorType result = end();
@@ -19807,9 +21194,9 @@ class basic_json
case value_t::string:
case value_t::binary:
{
- if (JSON_HEDLEY_UNLIKELY(not pos.m_it.primitive_iterator.is_begin()))
+ if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
{
- JSON_THROW(invalid_iterator::create(205, "iterator out of range"));
+ JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this));
}
if (is_string())
@@ -19845,7 +21232,7 @@ class basic_json
}
default:
- JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
}
return result;
@@ -19889,24 +21276,24 @@ class basic_json
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType_IteratorType}
- @sa @ref erase(IteratorType) -- removes the element at a given position
- @sa @ref erase(const typename object_t::key_type&) -- removes the element
+ @sa see @ref erase(IteratorType) -- removes the element at a given position
+ @sa see @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
- @sa @ref erase(const size_type) -- removes the element from an array at
+ @sa see @ref erase(const size_type) -- removes the element from an array at
the given index
@since version 1.0.0
*/
- template<class IteratorType, typename std::enable_if<
- std::is_same<IteratorType, typename basic_json_t::iterator>::value or
- std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
- = 0>
+ template < class IteratorType, typename std::enable_if <
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type
+ = 0 >
IteratorType erase(IteratorType first, IteratorType last)
{
// make sure iterator fits the current value
- if (JSON_HEDLEY_UNLIKELY(this != first.m_object or this != last.m_object))
+ if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
{
- JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value"));
+ JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this));
}
IteratorType result = end();
@@ -19920,10 +21307,10 @@ class basic_json
case value_t::string:
case value_t::binary:
{
- if (JSON_HEDLEY_LIKELY(not first.m_it.primitive_iterator.is_begin()
- or not last.m_it.primitive_iterator.is_end()))
+ if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
+ || !last.m_it.primitive_iterator.is_end()))
{
- JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this));
}
if (is_string())
@@ -19961,7 +21348,7 @@ class basic_json
}
default:
- JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
}
return result;
@@ -19988,10 +21375,10 @@ class basic_json
@liveexample{The example shows the effect of `erase()`.,erase__key_type}
- @sa @ref erase(IteratorType) -- removes the element at a given position
- @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
+ @sa see @ref erase(IteratorType) -- removes the element at a given position
+ @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
- @sa @ref erase(const size_type) -- removes the element from an array at
+ @sa see @ref erase(const size_type) -- removes the element from an array at
the given index
@since version 1.0.0
@@ -20004,7 +21391,7 @@ class basic_json
return m_value.object->erase(key);
}
- JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
}
/*!
@@ -20023,10 +21410,10 @@ class basic_json
@liveexample{The example shows the effect of `erase()`.,erase__size_type}
- @sa @ref erase(IteratorType) -- removes the element at a given position
- @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
+ @sa see @ref erase(IteratorType) -- removes the element at a given position
+ @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
- @sa @ref erase(const typename object_t::key_type&) -- removes the element
+ @sa see @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@since version 1.0.0
@@ -20038,14 +21425,14 @@ class basic_json
{
if (JSON_HEDLEY_UNLIKELY(idx >= size()))
{
- JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this));
}
m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
}
else
{
- JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
}
}
@@ -20079,7 +21466,7 @@ class basic_json
@liveexample{The example shows how `find()` is used.,find__key_type}
- @sa @ref contains(KeyT&&) const -- checks whether a key exists
+ @sa see @ref contains(KeyT&&) const -- checks whether a key exists
@since version 1.0.0
*/
@@ -20161,16 +21548,16 @@ class basic_json
@liveexample{The following code shows an example for `contains()`.,contains}
- @sa @ref find(KeyT&&) -- returns an iterator to an object element
- @sa @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer
+ @sa see @ref find(KeyT&&) -- returns an iterator to an object element
+ @sa see @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer
@since version 3.6.0
*/
- template<typename KeyT, typename std::enable_if<
- not std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int>::type = 0>
+ template < typename KeyT, typename std::enable_if <
+ !std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int >::type = 0 >
bool contains(KeyT && key) const
{
- return is_object() and m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();
+ return is_object() && m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();
}
/*!
@@ -20195,7 +21582,7 @@ class basic_json
@liveexample{The following code shows an example for `contains()`.,contains_json_pointer}
- @sa @ref contains(KeyT &&) const -- checks the existence of a key
+ @sa see @ref contains(KeyT &&) const -- checks the existence of a key
@since version 3.7.0
*/
@@ -20232,9 +21619,9 @@ class basic_json
@liveexample{The following code shows an example for `begin()`.,begin}
- @sa @ref cbegin() -- returns a const iterator to the beginning
- @sa @ref end() -- returns an iterator to the end
- @sa @ref cend() -- returns a const iterator to the end
+ @sa see @ref cbegin() -- returns a const iterator to the beginning
+ @sa see @ref end() -- returns an iterator to the end
+ @sa see @ref cend() -- returns a const iterator to the end
@since version 1.0.0
*/
@@ -20272,9 +21659,9 @@ class basic_json
@liveexample{The following code shows an example for `cbegin()`.,cbegin}
- @sa @ref begin() -- returns an iterator to the beginning
- @sa @ref end() -- returns an iterator to the end
- @sa @ref cend() -- returns a const iterator to the end
+ @sa see @ref begin() -- returns an iterator to the beginning
+ @sa see @ref end() -- returns an iterator to the end
+ @sa see @ref cend() -- returns a const iterator to the end
@since version 1.0.0
*/
@@ -20303,9 +21690,9 @@ class basic_json
@liveexample{The following code shows an example for `end()`.,end}
- @sa @ref cend() -- returns a const iterator to the end
- @sa @ref begin() -- returns an iterator to the beginning
- @sa @ref cbegin() -- returns a const iterator to the beginning
+ @sa see @ref cend() -- returns a const iterator to the end
+ @sa see @ref begin() -- returns an iterator to the beginning
+ @sa see @ref cbegin() -- returns a const iterator to the beginning
@since version 1.0.0
*/
@@ -20343,9 +21730,9 @@ class basic_json
@liveexample{The following code shows an example for `cend()`.,cend}
- @sa @ref end() -- returns an iterator to the end
- @sa @ref begin() -- returns an iterator to the beginning
- @sa @ref cbegin() -- returns a const iterator to the beginning
+ @sa see @ref end() -- returns an iterator to the end
+ @sa see @ref begin() -- returns an iterator to the beginning
+ @sa see @ref cbegin() -- returns a const iterator to the beginning
@since version 1.0.0
*/
@@ -20373,9 +21760,9 @@ class basic_json
@liveexample{The following code shows an example for `rbegin()`.,rbegin}
- @sa @ref crbegin() -- returns a const reverse iterator to the beginning
- @sa @ref rend() -- returns a reverse iterator to the end
- @sa @ref crend() -- returns a const reverse iterator to the end
+ @sa see @ref crbegin() -- returns a const reverse iterator to the beginning
+ @sa see @ref rend() -- returns a reverse iterator to the end
+ @sa see @ref crend() -- returns a const reverse iterator to the end
@since version 1.0.0
*/
@@ -20410,9 +21797,9 @@ class basic_json
@liveexample{The following code shows an example for `rend()`.,rend}
- @sa @ref crend() -- returns a const reverse iterator to the end
- @sa @ref rbegin() -- returns a reverse iterator to the beginning
- @sa @ref crbegin() -- returns a const reverse iterator to the beginning
+ @sa see @ref crend() -- returns a const reverse iterator to the end
+ @sa see @ref rbegin() -- returns a reverse iterator to the beginning
+ @sa see @ref crbegin() -- returns a const reverse iterator to the beginning
@since version 1.0.0
*/
@@ -20447,9 +21834,9 @@ class basic_json
@liveexample{The following code shows an example for `crbegin()`.,crbegin}
- @sa @ref rbegin() -- returns a reverse iterator to the beginning
- @sa @ref rend() -- returns a reverse iterator to the end
- @sa @ref crend() -- returns a const reverse iterator to the end
+ @sa see @ref rbegin() -- returns a reverse iterator to the beginning
+ @sa see @ref rend() -- returns a reverse iterator to the end
+ @sa see @ref crend() -- returns a const reverse iterator to the end
@since version 1.0.0
*/
@@ -20476,9 +21863,9 @@ class basic_json
@liveexample{The following code shows an example for `crend()`.,crend}
- @sa @ref rend() -- returns a reverse iterator to the end
- @sa @ref rbegin() -- returns a reverse iterator to the beginning
- @sa @ref crbegin() -- returns a const reverse iterator to the beginning
+ @sa see @ref rend() -- returns a reverse iterator to the end
+ @sa see @ref rbegin() -- returns a reverse iterator to the beginning
+ @sa see @ref crbegin() -- returns a const reverse iterator to the beginning
@since version 1.0.0
*/
@@ -20689,7 +22076,7 @@ class basic_json
- The complexity is constant.
- Has the semantics of `begin() == end()`.
- @sa @ref size() -- returns the number of elements
+ @sa see @ref size() -- returns the number of elements
@since version 1.0.0
*/
@@ -20761,8 +22148,8 @@ class basic_json
- The complexity is constant.
- Has the semantics of `std::distance(begin(), end())`.
- @sa @ref empty() -- checks whether the container is empty
- @sa @ref max_size() -- returns the maximal number of elements
+ @sa see @ref empty() -- checks whether the container is empty
+ @sa see @ref max_size() -- returns the maximal number of elements
@since version 1.0.0
*/
@@ -20833,7 +22220,7 @@ class basic_json
- Has the semantics of returning `b.size()` where `b` is the largest
possible JSON value.
- @sa @ref size() -- returns the number of elements
+ @sa see @ref size() -- returns the number of elements
@since version 1.0.0
*/
@@ -20903,7 +22290,7 @@ class basic_json
@exceptionsafety No-throw guarantee: this function never throws exceptions.
- @sa @ref basic_json(value_t) -- constructor that creates an object with the
+ @sa see @ref basic_json(value_t) -- constructor that creates an object with the
same value than calling `clear()`
@since version 1.0.0
@@ -20988,9 +22375,9 @@ class basic_json
void push_back(basic_json&& val)
{
// push_back only works for null objects or arrays
- if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_array())))
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{
- JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this));
}
// transform null object into an array
@@ -21003,6 +22390,7 @@ class basic_json
// add element to array (move semantics)
m_value.array->push_back(std::move(val));
+ set_parent(m_value.array->back());
// if val is moved from, basic_json move constructor marks it null so we do not call the destructor
}
@@ -21023,9 +22411,9 @@ class basic_json
void push_back(const basic_json& val)
{
// push_back only works for null objects or arrays
- if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_array())))
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{
- JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this));
}
// transform null object into an array
@@ -21038,6 +22426,7 @@ class basic_json
// add element to array
m_value.array->push_back(val);
+ set_parent(m_value.array->back());
}
/*!
@@ -21073,9 +22462,9 @@ class basic_json
void push_back(const typename object_t::value_type& val)
{
// push_back only works for null objects or objects
- if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_object())))
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{
- JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this));
}
// transform null object into an object
@@ -21086,8 +22475,9 @@ class basic_json
assert_invariant();
}
- // add element to array
- m_value.object->insert(val);
+ // add element to object
+ auto res = m_value.object->insert(val);
+ set_parent(res.first->second);
}
/*!
@@ -21127,7 +22517,7 @@ class basic_json
*/
void push_back(initializer_list_t init)
{
- if (is_object() and init.size() == 2 and (*init.begin())->is_string())
+ if (is_object() && init.size() == 2 && (*init.begin())->is_string())
{
basic_json&& key = init.begin()->moved_or_copied();
push_back(typename object_t::value_type(
@@ -21176,9 +22566,9 @@ class basic_json
reference emplace_back(Args&& ... args)
{
// emplace_back only works for null objects or arrays
- if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_array())))
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{
- JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this));
}
// transform null object into an array
@@ -21191,10 +22581,10 @@ class basic_json
// add element to array (perfect forwarding)
#ifdef JSON_HAS_CPP_17
- return m_value.array->emplace_back(std::forward<Args>(args)...);
+ return set_parent(m_value.array->emplace_back(std::forward<Args>(args)...));
#else
m_value.array->emplace_back(std::forward<Args>(args)...);
- return m_value.array->back();
+ return set_parent(m_value.array->back());
#endif
}
@@ -21229,9 +22619,9 @@ class basic_json
std::pair<iterator, bool> emplace(Args&& ... args)
{
// emplace only works for null objects or arrays
- if (JSON_HEDLEY_UNLIKELY(not(is_null() or is_object())))
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{
- JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this));
}
// transform null object into an object
@@ -21244,6 +22634,8 @@ class basic_json
// add element to array (perfect forwarding)
auto res = m_value.object->emplace(std::forward<Args>(args)...);
+ set_parent(res.first->second);
+
// create result iterator and set iterator to the result of emplace
auto it = begin();
it.m_it.object_iterator = res.first;
@@ -21259,7 +22651,7 @@ class basic_json
iterator insert_iterator(const_iterator pos, Args&& ... args)
{
iterator result(this);
- assert(m_value.array != nullptr);
+ JSON_ASSERT(m_value.array != nullptr);
auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);
m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);
@@ -21302,14 +22694,14 @@ class basic_json
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
- JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
// insert to array and return iterator
- return insert_iterator(pos, val);
+ return set_parents(insert_iterator(pos, val), static_cast<typename iterator::difference_type>(1));
}
- JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
/*!
@@ -21353,14 +22745,14 @@ class basic_json
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
- JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
// insert to array and return iterator
- return insert_iterator(pos, cnt, val);
+ return set_parents(insert_iterator(pos, cnt, val), static_cast<typename iterator::difference_type>(cnt));
}
- JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
/*!
@@ -21396,30 +22788,30 @@ class basic_json
iterator insert(const_iterator pos, const_iterator first, const_iterator last)
{
// insert only works for arrays
- if (JSON_HEDLEY_UNLIKELY(not is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!is_array()))
{
- JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
- JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
// check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
- JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this));
}
if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
{
- JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
+ JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this));
}
// insert to array and return iterator
- return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
+ return set_parents(insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator), std::distance(first, last));
}
/*!
@@ -21449,19 +22841,19 @@ class basic_json
iterator insert(const_iterator pos, initializer_list_t ilist)
{
// insert only works for arrays
- if (JSON_HEDLEY_UNLIKELY(not is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!is_array()))
{
- JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
- JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
// insert to array and return iterator
- return insert_iterator(pos, ilist.begin(), ilist.end());
+ return set_parents(insert_iterator(pos, ilist.begin(), ilist.end()), static_cast<typename iterator::difference_type>(ilist.size()));
}
/*!
@@ -21490,21 +22882,21 @@ class basic_json
void insert(const_iterator first, const_iterator last)
{
// insert only works for objects
- if (JSON_HEDLEY_UNLIKELY(not is_object()))
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
- JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
// check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
- JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this));
}
// passed iterators must belong to objects
- if (JSON_HEDLEY_UNLIKELY(not first.m_object->is_object()))
+ if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
{
- JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this));
}
m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
@@ -21539,13 +22931,13 @@ class basic_json
assert_invariant();
}
- if (JSON_HEDLEY_UNLIKELY(not is_object()))
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
- JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this));
}
- if (JSON_HEDLEY_UNLIKELY(not j.is_object()))
+ if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{
- JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name())));
+ JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), *this));
}
for (auto it = j.cbegin(); it != j.cend(); ++it)
@@ -21590,22 +22982,22 @@ class basic_json
assert_invariant();
}
- if (JSON_HEDLEY_UNLIKELY(not is_object()))
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
- JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this));
}
// check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
- JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this));
}
// passed iterators must belong to objects
- if (JSON_HEDLEY_UNLIKELY(not first.m_object->is_object()
- or not last.m_object->is_object()))
+ if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()
+ || !last.m_object->is_object()))
{
- JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this));
}
for (auto it = first; it != last; ++it)
@@ -21632,20 +23024,51 @@ class basic_json
@since version 1.0.0
*/
void swap(reference other) noexcept (
- std::is_nothrow_move_constructible<value_t>::value and
- std::is_nothrow_move_assignable<value_t>::value and
- std::is_nothrow_move_constructible<json_value>::value and
+ std::is_nothrow_move_constructible<value_t>::value&&
+ std::is_nothrow_move_assignable<value_t>::value&&
+ std::is_nothrow_move_constructible<json_value>::value&&
std::is_nothrow_move_assignable<json_value>::value
)
{
std::swap(m_type, other.m_type);
std::swap(m_value, other.m_value);
+
+ set_parents();
+ other.set_parents();
assert_invariant();
}
/*!
@brief exchanges the values
+ Exchanges the contents of the JSON value from @a left with those of @a right. Does not
+ invoke any move, copy, or swap operations on individual elements. All
+ iterators and references remain valid. The past-the-end iterator is
+ invalidated. implemented as a friend function callable via ADL.
+
+ @param[in,out] left JSON value to exchange the contents with
+ @param[in,out] right JSON value to exchange the contents with
+
+ @complexity Constant.
+
+ @liveexample{The example below shows how JSON values can be swapped with
+ `swap()`.,swap__reference}
+
+ @since version 1.0.0
+ */
+ friend void swap(reference left, reference right) noexcept (
+ std::is_nothrow_move_constructible<value_t>::value&&
+ std::is_nothrow_move_assignable<value_t>::value&&
+ std::is_nothrow_move_constructible<json_value>::value&&
+ std::is_nothrow_move_assignable<json_value>::value
+ )
+ {
+ left.swap(right);
+ }
+
+ /*!
+ @brief exchanges the values
+
Exchanges the contents of a JSON array with those of @a other. Does not
invoke any move, copy, or swap operations on individual elements. All
iterators and references remain valid. The past-the-end iterator is
@@ -21663,7 +23086,7 @@ class basic_json
@since version 1.0.0
*/
- void swap(array_t& other)
+ void swap(array_t& other) // NOLINT(bugprone-exception-escape)
{
// swap only works for arrays
if (JSON_HEDLEY_LIKELY(is_array()))
@@ -21672,7 +23095,7 @@ class basic_json
}
else
{
- JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
@@ -21696,7 +23119,7 @@ class basic_json
@since version 1.0.0
*/
- void swap(object_t& other)
+ void swap(object_t& other) // NOLINT(bugprone-exception-escape)
{
// swap only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
@@ -21705,7 +23128,7 @@ class basic_json
}
else
{
- JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
@@ -21729,7 +23152,7 @@ class basic_json
@since version 1.0.0
*/
- void swap(string_t& other)
+ void swap(string_t& other) // NOLINT(bugprone-exception-escape)
{
// swap only works for strings
if (JSON_HEDLEY_LIKELY(is_string()))
@@ -21738,7 +23161,7 @@ class basic_json
}
else
{
- JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
@@ -21762,7 +23185,7 @@ class basic_json
@since version 3.8.0
*/
- void swap(binary_t& other)
+ void swap(binary_t& other) // NOLINT(bugprone-exception-escape)
{
// swap only works for strings
if (JSON_HEDLEY_LIKELY(is_binary()))
@@ -21771,12 +23194,12 @@ class basic_json
}
else
{
- JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
- /// @copydoc swap(binary_t)
- void swap(typename binary_t::container_type& other)
+ /// @copydoc swap(binary_t&)
+ void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)
{
// swap only works for strings
if (JSON_HEDLEY_LIKELY(is_binary()))
@@ -21785,7 +23208,7 @@ class basic_json
}
else
{
- JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
@@ -21894,27 +23317,27 @@ class basic_json
return false;
}
}
- else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
+ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;
}
- else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)
+ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
{
return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
}
- else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)
+ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;
}
- else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)
+ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);
}
- else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)
+ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
{
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;
}
- else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)
+ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);
}
@@ -21928,7 +23351,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept
+ friend bool operator==(const_reference lhs, ScalarType rhs) noexcept
{
return lhs == basic_json(rhs);
}
@@ -21939,7 +23362,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept
+ friend bool operator==(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) == rhs;
}
@@ -21964,7 +23387,7 @@ class basic_json
*/
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
{
- return not (lhs == rhs);
+ return !(lhs == rhs);
}
/*!
@@ -21973,7 +23396,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept
+ friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept
{
return lhs != basic_json(rhs);
}
@@ -21984,7 +23407,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept
+ friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) != rhs;
}
@@ -22057,27 +23480,27 @@ class basic_json
return false;
}
}
- else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
+ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;
}
- else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)
+ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
{
return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);
}
- else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)
+ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
}
- else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)
+ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);
}
- else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)
+ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);
}
- else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)
+ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
{
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
}
@@ -22094,7 +23517,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept
+ friend bool operator<(const_reference lhs, ScalarType rhs) noexcept
{
return lhs < basic_json(rhs);
}
@@ -22105,7 +23528,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept
+ friend bool operator<(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) < rhs;
}
@@ -22131,7 +23554,7 @@ class basic_json
*/
friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
{
- return not (rhs < lhs);
+ return !(rhs < lhs);
}
/*!
@@ -22140,7 +23563,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept
+ friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept
{
return lhs <= basic_json(rhs);
}
@@ -22151,7 +23574,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept
+ friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) <= rhs;
}
@@ -22177,7 +23600,7 @@ class basic_json
*/
friend bool operator>(const_reference lhs, const_reference rhs) noexcept
{
- return not (lhs <= rhs);
+ return !(lhs <= rhs);
}
/*!
@@ -22186,7 +23609,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept
+ friend bool operator>(const_reference lhs, ScalarType rhs) noexcept
{
return lhs > basic_json(rhs);
}
@@ -22197,7 +23620,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept
+ friend bool operator>(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) > rhs;
}
@@ -22223,7 +23646,7 @@ class basic_json
*/
friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
{
- return not (lhs < rhs);
+ return !(lhs < rhs);
}
/*!
@@ -22232,7 +23655,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept
+ friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept
{
return lhs >= basic_json(rhs);
}
@@ -22243,7 +23666,7 @@ class basic_json
*/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
- friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept
+ friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) >= rhs;
}
@@ -22344,6 +23767,9 @@ class basic_json
(optional)
@param[in] allow_exceptions whether to throw exceptions in case of a
parse error (optional, true by default)
+ @param[in] ignore_comments whether comments should be ignored and treated
+ like whitespace (true) or yield a parse error (true); (optional, false by
+ default)
@return deserialized JSON value; in case of a parse error and
@a allow_exceptions set to `false`, the return value will be
@@ -22372,16 +23798,18 @@ class basic_json
@liveexample{The example below demonstrates the `parse()` function reading
from a contiguous container.,parse__contiguouscontainer__parser_callback_t}
- @since version 2.0.3 (contiguous containers)
+ @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to
+ ignore comments.
*/
template<typename InputType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json parse(InputType&& i,
const parser_callback_t cb = nullptr,
- const bool allow_exceptions = true)
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false)
{
basic_json result;
- parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions).parse(true, result);
+ parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);
return result;
}
@@ -22398,6 +23826,9 @@ class basic_json
(optional)
@param[in] allow_exceptions whether to throw exceptions in case of a
parse error (optional, true by default)
+ @param[in] ignore_comments whether comments should be ignored and treated
+ like whitespace (true) or yield a parse error (true); (optional, false by
+ default)
@return deserialized JSON value; in case of a parse error and
@a allow_exceptions set to `false`, the return value will be
@@ -22413,10 +23844,11 @@ class basic_json
static basic_json parse(IteratorType first,
IteratorType last,
const parser_callback_t cb = nullptr,
- const bool allow_exceptions = true)
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false)
{
basic_json result;
- parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions).parse(true, result);
+ parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);
return result;
}
@@ -22424,10 +23856,11 @@ class basic_json
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
static basic_json parse(detail::span_input_adapter&& i,
const parser_callback_t cb = nullptr,
- const bool allow_exceptions = true)
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false)
{
basic_json result;
- parser(i.get(), cb, allow_exceptions).parse(true, result);
+ parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);
return result;
}
@@ -22447,6 +23880,9 @@ class basic_json
iterators.
@param[in] i input to read from
+ @param[in] ignore_comments whether comments should be ignored and treated
+ like whitespace (true) or yield a parse error (true); (optional, false by
+ default)
@return Whether the input read from @a i is valid JSON.
@@ -22459,22 +23895,25 @@ class basic_json
from a string.,accept__string}
*/
template<typename InputType>
- static bool accept(InputType&& i)
+ static bool accept(InputType&& i,
+ const bool ignore_comments = false)
{
- return parser(detail::input_adapter(std::forward<InputType>(i))).accept(true);
+ return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);
}
template<typename IteratorType>
- static bool accept(IteratorType first, IteratorType last)
+ static bool accept(IteratorType first, IteratorType last,
+ const bool ignore_comments = false)
{
- return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true);
+ return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);
}
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
- static bool accept(detail::span_input_adapter&& i)
+ static bool accept(detail::span_input_adapter&& i,
+ const bool ignore_comments = false)
{
- return parser(i.get()).accept(true);
+ return parser(i.get(), nullptr, false, ignore_comments).accept(true);
}
/*!
@@ -22494,6 +23933,9 @@ class basic_json
@param[in,out] sax SAX event listener
@param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON)
@param[in] strict whether the input has to be consumed completely
+ @param[in] ignore_comments whether comments should be ignored and treated
+ like whitespace (true) or yield a parse error (true); (optional, false by
+ default); only applies to the JSON file format.
@return return value of the last processed SAX event
@@ -22518,11 +23960,12 @@ class basic_json
JSON_HEDLEY_NON_NULL(2)
static bool sax_parse(InputType&& i, SAX* sax,
input_format_t format = input_format_t::json,
- const bool strict = true)
+ const bool strict = true,
+ const bool ignore_comments = false)
{
auto ia = detail::input_adapter(std::forward<InputType>(i));
return format == input_format_t::json
- ? parser(std::move(ia)).sax_parse(sax, strict)
+ ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);
}
@@ -22530,11 +23973,12 @@ class basic_json
JSON_HEDLEY_NON_NULL(3)
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
input_format_t format = input_format_t::json,
- const bool strict = true)
+ const bool strict = true,
+ const bool ignore_comments = false)
{
auto ia = detail::input_adapter(std::move(first), std::move(last));
return format == input_format_t::json
- ? parser(std::move(ia)).sax_parse(sax, strict)
+ ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);
}
@@ -22543,11 +23987,14 @@ class basic_json
JSON_HEDLEY_NON_NULL(2)
static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,
input_format_t format = input_format_t::json,
- const bool strict = true)
+ const bool strict = true,
+ const bool ignore_comments = false)
{
auto ia = i.get();
return format == input_format_t::json
- ? parser(std::move(ia)).sax_parse(sax, strict)
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);
}
@@ -22627,8 +24074,8 @@ class basic_json
@liveexample{The following code exemplifies `type_name()` for all JSON
types.,type_name}
- @sa @ref type() -- return the type of the JSON value
- @sa @ref operator value_t() -- return the type of the JSON value (implicit)
+ @sa see @ref type() -- return the type of the JSON value
+ @sa see @ref operator value_t() -- return the type of the JSON value (implicit)
@since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`
since 3.0.0
@@ -22660,7 +24107,7 @@ class basic_json
}
- private:
+ JSON_PRIVATE_UNLESS_TESTED:
//////////////////////
// member variables //
//////////////////////
@@ -22671,6 +24118,11 @@ class basic_json
/// the value of the current element
json_value m_value = {};
+#if JSON_DIAGNOSTICS
+ /// a pointer to a parent value (for debugging purposes)
+ basic_json* m_parent = nullptr;
+#endif
+
//////////////////////////////////////////
// binary serialization/deserialization //
//////////////////////////////////////////
@@ -22749,7 +24201,6 @@ class basic_json
- bignum (0xC2..0xC3)
- decimal fraction (0xC4)
- bigfloat (0xC5)
- - tagged items (0xC6..0xD4, 0xD8..0xDB)
- expected conversions (0xD5..0xD7)
- simple values (0xE0..0xF3, 0xF8)
- undefined (0xF7)
@@ -22765,10 +24216,10 @@ class basic_json
vector in CBOR format.,to_cbor}
@sa http://cbor.io
- @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+ @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the
analogous deserialization
- @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
- @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
+ @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format
+ @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the
related UBJSON format
@since version 2.0.9; compact representation of floating-point numbers
@@ -22821,7 +24272,8 @@ class basic_json
number_unsigned | 256..65535 | uint 16 | 0xCD
number_unsigned | 65536..4294967295 | uint 32 | 0xCE
number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF
- number_float | *any value* | float 64 | 0xCB
+ number_float | *any value representable by a float* | float 32 | 0xCA
+ number_float | *any value NOT representable by a float* | float 64 | 0xCB
string | *length*: 0..31 | fixstr | 0xA0..0xBF
string | *length*: 32..255 | str 8 | 0xD9
string | *length*: 256..65535 | str 16 | 0xDA
@@ -22845,9 +24297,6 @@ class basic_json
- arrays with more than 4294967295 elements
- objects with more than 4294967295 elements
- @note The following MessagePack types are not used in the conversion:
- - float 32 (0xCA)
-
@note Any MessagePack output created @ref to_msgpack can be successfully
parsed by @ref from_msgpack.
@@ -22864,9 +24313,9 @@ class basic_json
vector in MessagePack format.,to_msgpack}
@sa http://msgpack.org
- @sa @ref from_msgpack for the analogous deserialization
- @sa @ref to_cbor(const basic_json& for the related CBOR format
- @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
+ @sa see @ref from_msgpack for the analogous deserialization
+ @sa see @ref to_cbor(const basic_json& for the related CBOR format
+ @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the
related UBJSON format
@since version 2.0.9
@@ -22916,6 +24365,7 @@ class basic_json
number_unsigned | 256..32767 | int16 | `I`
number_unsigned | 32768..2147483647 | int32 | `l`
number_unsigned | 2147483648..9223372036854775807 | int64 | `L`
+ number_unsigned | 2147483649..18446744073709551615 | high-precision | `H`
number_float | *any value* | float64 | `D`
string | *with shortest length indicator* | string | `S`
array | *see notes on optimized format* | array | `[`
@@ -22926,7 +24376,6 @@ class basic_json
@note The following values can **not** be converted to a UBJSON value:
- strings with more than 9223372036854775807 bytes (theoretical)
- - unsigned integer numbers above 9223372036854775807
@note The following markers are not used in the conversion:
- `Z`: no-op values are not created.
@@ -22967,10 +24416,10 @@ class basic_json
vector in UBJSON format.,to_ubjson}
@sa http://ubjson.org
- @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the
+ @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the
analogous deserialization
- @sa @ref to_cbor(const basic_json& for the related CBOR format
- @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
+ @sa see @ref to_cbor(const basic_json& for the related CBOR format
+ @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format
@since version 3.1.0
*/
@@ -23045,12 +24494,12 @@ class basic_json
vector in BSON format.,to_bson}
@sa http://bsonspec.org/spec.html
- @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the
+ @sa see @ref from_bson(detail::input_adapter&&, const bool strict) for the
analogous deserialization
- @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
+ @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the
related UBJSON format
- @sa @ref to_cbor(const basic_json&) for the related CBOR format
- @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
+ @sa see @ref to_cbor(const basic_json&) for the related CBOR format
+ @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format
*/
static std::vector<uint8_t> to_bson(const basic_json& j)
{
@@ -23065,7 +24514,7 @@ class basic_json
@param j The JSON object to convert to BSON.
@param o The output adapter that receives the binary BSON representation.
@pre The input `j` shall be an object: `j.is_object() == true`
- @sa @ref to_bson(const basic_json&)
+ @sa see @ref to_bson(const basic_json&)
*/
static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)
{
@@ -23138,7 +24587,6 @@ class basic_json
- bignum (0xC2..0xC3)
- decimal fraction (0xC4)
- bigfloat (0xC5)
- - tagged items (0xC6..0xD4, 0xD8..0xDB)
- expected conversions (0xD5..0xD7)
- simple values (0xE0..0xF3, 0xF8)
- undefined (0xF7)
@@ -23155,6 +24603,7 @@ class basic_json
(true by default)
@param[in] allow_exceptions whether to throw exceptions in case of a
parse error (optional, true by default)
+ @param[in] tag_handler how to treat CBOR tags (optional, error by default)
@return deserialized JSON value; in case of a parse error and
@a allow_exceptions set to `false`, the return value will be
@@ -23172,43 +24621,45 @@ class basic_json
format to a JSON value.,from_cbor}
@sa http://cbor.io
- @sa @ref to_cbor(const basic_json&) for the analogous serialization
- @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the
+ @sa see @ref to_cbor(const basic_json&) for the analogous serialization
+ @sa see @ref from_msgpack(InputType&&, const bool, const bool) for the
related MessagePack format
- @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the
+ @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the
related UBJSON format
@since version 2.0.9; parameter @a start_index since 2.1.1; changed to
consume input adapters, removed start_index parameter, and added
@a strict parameter since 3.0.0; added @a allow_exceptions parameter
- since 3.2.0
+ since 3.2.0; added @a tag_handler parameter since 3.9.0.
*/
template<typename InputType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_cbor(InputType&& i,
const bool strict = true,
- const bool allow_exceptions = true)
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
auto ia = detail::input_adapter(std::forward<InputType>(i));
- const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
return res ? result : basic_json(value_t::discarded);
}
/*!
- @copydoc from_cbor(detail::input_adapter&&, const bool, const bool)
+ @copydoc from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t)
*/
template<typename IteratorType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_cbor(IteratorType first, IteratorType last,
const bool strict = true,
- const bool allow_exceptions = true)
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
auto ia = detail::input_adapter(std::move(first), std::move(last));
- const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
return res ? result : basic_json(value_t::discarded);
}
@@ -23217,9 +24668,10 @@ class basic_json
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
static basic_json from_cbor(const T* ptr, std::size_t len,
const bool strict = true,
- const bool allow_exceptions = true)
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
- return from_cbor(ptr, ptr + len, strict, allow_exceptions);
+ return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
}
@@ -23227,12 +24679,14 @@ class basic_json
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
static basic_json from_cbor(detail::span_input_adapter&& i,
const bool strict = true,
- const bool allow_exceptions = true)
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
{
basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
auto ia = i.get();
- const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
return res ? result : basic_json(value_t::discarded);
}
@@ -23309,12 +24763,12 @@ class basic_json
MessagePack format to a JSON value.,from_msgpack}
@sa http://msgpack.org
- @sa @ref to_msgpack(const basic_json&) for the analogous serialization
- @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+ @sa see @ref to_msgpack(const basic_json&) for the analogous serialization
+ @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the
related CBOR format
- @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for
+ @sa see @ref from_ubjson(InputType&&, const bool, const bool) for
the related UBJSON format
- @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for
+ @sa see @ref from_bson(InputType&&, const bool, const bool) for
the related BSON format
@since version 2.0.9; parameter @a start_index since 2.1.1; changed to
@@ -23336,7 +24790,7 @@ class basic_json
}
/*!
- @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool)
+ @copydoc from_msgpack(InputType&&, const bool, const bool)
*/
template<typename IteratorType>
JSON_HEDLEY_WARN_UNUSED_RESULT
@@ -23371,6 +24825,7 @@ class basic_json
basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
auto ia = i.get();
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);
return res ? result : basic_json(value_t::discarded);
}
@@ -23397,6 +24852,7 @@ class basic_json
int16 | number_integer | `I`
int32 | number_integer | `l`
int64 | number_integer | `L`
+ high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H'
string | string | `S`
char | string | `C`
array | array (optimized values are supported) | `[`
@@ -23426,13 +24882,13 @@ class basic_json
UBJSON format to a JSON value.,from_ubjson}
@sa http://ubjson.org
- @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
+ @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the
analogous serialization
- @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+ @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the
related CBOR format
- @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for
+ @sa see @ref from_msgpack(InputType&&, const bool, const bool) for
the related MessagePack format
- @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for
+ @sa see @ref from_bson(InputType&&, const bool, const bool) for
the related BSON format
@since version 3.1.0; added @a allow_exceptions parameter since 3.2.0
@@ -23451,7 +24907,7 @@ class basic_json
}
/*!
- @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool)
+ @copydoc from_ubjson(InputType&&, const bool, const bool)
*/
template<typename IteratorType>
JSON_HEDLEY_WARN_UNUSED_RESULT
@@ -23485,6 +24941,7 @@ class basic_json
basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
auto ia = i.get();
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);
return res ? result : basic_json(value_t::discarded);
}
@@ -23504,7 +24961,7 @@ class basic_json
string | 0x02 | string
document | 0x03 | object
array | 0x04 | array
- binary | 0x05 | still unsupported
+ binary | 0x05 | binary
undefined | 0x06 | still unsupported
ObjectId | 0x07 | still unsupported
boolean | 0x08 | boolean
@@ -23542,12 +24999,12 @@ class basic_json
BSON format to a JSON value.,from_bson}
@sa http://bsonspec.org/spec.html
- @sa @ref to_bson(const basic_json&) for the analogous serialization
- @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+ @sa see @ref to_bson(const basic_json&) for the analogous serialization
+ @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the
related CBOR format
- @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for
+ @sa see @ref from_msgpack(InputType&&, const bool, const bool) for
the related MessagePack format
- @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the
+ @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the
related UBJSON format
*/
template<typename InputType>
@@ -23564,7 +25021,7 @@ class basic_json
}
/*!
- @copydoc from_bson(detail::input_adapter&&, const bool, const bool)
+ @copydoc from_bson(InputType&&, const bool, const bool)
*/
template<typename IteratorType>
JSON_HEDLEY_WARN_UNUSED_RESULT
@@ -23598,6 +25055,7 @@ class basic_json
basic_json result;
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
auto ia = i.get();
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);
return res ? result : basic_json(value_t::discarded);
}
@@ -23780,7 +25238,7 @@ class basic_json
@liveexample{The following code shows how a JSON object is flattened to an
object whose keys consist of JSON pointers.,flatten}
- @sa @ref unflatten() for the reverse function
+ @sa see @ref unflatten() for the reverse function
@since version 2.0.0
*/
@@ -23817,7 +25275,7 @@ class basic_json
@liveexample{The following code shows how a flattened JSON object is
unflattened into the original nested JSON object.,unflatten}
- @sa @ref flatten() for the reverse function
+ @sa see @ref flatten() for the reverse function
@since version 2.0.0
*/
@@ -23875,7 +25333,7 @@ class basic_json
@liveexample{The following code shows how a JSON patch is applied to a
value.,patch}
- @sa @ref diff -- create a JSON patch by comparing two JSON values
+ @sa see @ref diff -- create a JSON patch by comparing two JSON values
@sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
@sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)
@@ -23962,10 +25420,10 @@ class basic_json
else
{
const auto idx = json_pointer::array_index(last_path);
- if (JSON_HEDLEY_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
+ if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
{
// avoid undefined behavior
- JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent));
}
// default case: insert add offset
@@ -23976,12 +25434,12 @@ class basic_json
// if there exists a parent it cannot be primitive
default: // LCOV_EXCL_LINE
- assert(false); // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
};
// wrapper for "remove" operation; remove value at ptr
- const auto operation_remove = [&result](json_pointer & ptr)
+ const auto operation_remove = [this, &result](json_pointer & ptr)
{
// get reference to parent of JSON pointer ptr
const auto last_path = ptr.back();
@@ -23999,20 +25457,20 @@ class basic_json
}
else
{
- JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found"));
+ JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this));
}
}
else if (parent.is_array())
{
// note erase performs range check
- parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
+ parent.erase(json_pointer::array_index(last_path));
}
};
// type check: top level value must be an array
- if (JSON_HEDLEY_UNLIKELY(not json_patch.is_array()))
+ if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
{
- JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch));
}
// iterate and apply the operations
@@ -24032,13 +25490,15 @@ class basic_json
// check if desired value is present
if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))
{
- JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'"));
+ // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+ JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val));
}
// check if result is of type string
- if (JSON_HEDLEY_UNLIKELY(string_type and not it->second.is_string()))
+ if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
{
- JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'"));
+ // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+ JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val));
}
// no error: return value
@@ -24046,14 +25506,14 @@ class basic_json
};
// type check: every element of the array must be an object
- if (JSON_HEDLEY_UNLIKELY(not val.is_object()))
+ if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
{
- JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val));
}
// collect mandatory members
- const std::string op = get_value("op", "op", true);
- const std::string path = get_value(op, "path", true);
+ const auto op = get_value("op", "op", true).template get<std::string>();
+ const auto path = get_value(op, "path", true).template get<std::string>();
json_pointer ptr(path);
switch (get_op(op))
@@ -24079,7 +25539,7 @@ class basic_json
case patch_operations::move:
{
- const std::string from_path = get_value("move", "from", true);
+ const auto from_path = get_value("move", "from", true).template get<std::string>();
json_pointer from_ptr(from_path);
// the "from" location must exist - use at()
@@ -24096,7 +25556,7 @@ class basic_json
case patch_operations::copy:
{
- const std::string from_path = get_value("copy", "from", true);
+ const auto from_path = get_value("copy", "from", true).template get<std::string>();
const json_pointer from_ptr(from_path);
// the "from" location must exist - use at()
@@ -24124,9 +25584,9 @@ class basic_json
}
// throw an exception if test fails
- if (JSON_HEDLEY_UNLIKELY(not success))
+ if (JSON_HEDLEY_UNLIKELY(!success))
{
- JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump()));
+ JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val));
}
break;
@@ -24136,7 +25596,7 @@ class basic_json
{
// op must be "add", "remove", "replace", "move", "copy", or
// "test"
- JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid"));
+ JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val));
}
}
}
@@ -24170,8 +25630,8 @@ class basic_json
@liveexample{The following code shows how a JSON patch is created as a
diff for two JSON values.,diff}
- @sa @ref patch -- apply a JSON patch
- @sa @ref merge_patch -- apply a JSON Merge Patch
+ @sa see @ref patch -- apply a JSON patch
+ @sa see @ref merge_patch -- apply a JSON Merge Patch
@sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
@@ -24206,7 +25666,7 @@ class basic_json
{
// first pass: traverse common elements
std::size_t i = 0;
- while (i < source.size() and i < target.size())
+ while (i < source.size() && i < target.size())
{
// recursive call to compare array values at index i
auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i));
@@ -24252,12 +25712,12 @@ class basic_json
for (auto it = source.cbegin(); it != source.cend(); ++it)
{
// escape the key name to be used in a JSON patch
- const auto key = json_pointer::escape(it.key());
+ const auto path_key = path + "/" + detail::escape(it.key());
if (target.find(it.key()) != target.end())
{
// recursive call to compare object values at key it
- auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key);
+ auto temp_diff = diff(it.value(), target[it.key()], path_key);
result.insert(result.end(), temp_diff.begin(), temp_diff.end());
}
else
@@ -24265,7 +25725,7 @@ class basic_json
// found a key that is not in o -> remove it
result.push_back(object(
{
- {"op", "remove"}, {"path", path + "/" + key}
+ {"op", "remove"}, {"path", path_key}
}));
}
}
@@ -24276,10 +25736,10 @@ class basic_json
if (source.find(it.key()) == source.end())
{
// found a key that is not in this -> add it
- const auto key = json_pointer::escape(it.key());
+ const auto path_key = path + "/" + detail::escape(it.key());
result.push_back(
{
- {"op", "add"}, {"path", path + "/" + key},
+ {"op", "add"}, {"path", path_key},
{"value", it.value()}
});
}
@@ -24348,7 +25808,7 @@ class basic_json
@liveexample{The following code shows how a JSON Merge Patch is applied to
a JSON document.,merge_patch}
- @sa @ref patch -- apply a JSON patch
+ @sa see @ref patch -- apply a JSON patch
@sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)
@since version 3.0.0
@@ -24357,7 +25817,7 @@ class basic_json
{
if (apply_patch.is_object())
{
- if (not is_object())
+ if (!is_object())
{
*this = object();
}
@@ -24417,9 +25877,7 @@ struct hash<nlohmann::json>
*/
std::size_t operator()(const nlohmann::json& j) const
{
- // a naive hashing via the string representation
- const auto& h = hash<nlohmann::json::string_t>();
- return h(j.dump());
+ return nlohmann::detail::hash(j);
}
};
@@ -24440,20 +25898,25 @@ struct less<::nlohmann::detail::value_t>
}
};
+// C++20 prohibit function specialization in the std namespace.
+#ifndef JSON_HAS_CPP_20
+
/*!
@brief exchanges the values of two JSON objects
@since version 1.0.0
*/
template<>
-inline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept(
- is_nothrow_move_constructible<nlohmann::json>::value and
+inline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name)
+ is_nothrow_move_constructible<nlohmann::json>::value&& // NOLINT(misc-redundant-expression)
is_nothrow_move_assignable<nlohmann::json>::value
-)
+ )
{
j1.swap(j2);
}
+#endif
+
} // namespace std
/*!
@@ -24498,24 +25961,28 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
// restore GCC/clang diagnostic settings
-#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
- #pragma GCC diagnostic pop
-#endif
#if defined(__clang__)
#pragma GCC diagnostic pop
#endif
// clean up
+#undef JSON_ASSERT
#undef JSON_INTERNAL_CATCH
#undef JSON_CATCH
#undef JSON_THROW
#undef JSON_TRY
+#undef JSON_PRIVATE_UNLESS_TESTED
+#undef JSON_HAS_CPP_11
#undef JSON_HAS_CPP_14
#undef JSON_HAS_CPP_17
+#undef JSON_HAS_CPP_20
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
#undef NLOHMANN_BASIC_JSON_TPL
+#undef JSON_EXPLICIT
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
+
+
#undef JSON_HEDLEY_ALWAYS_INLINE
#undef JSON_HEDLEY_ARM_VERSION
#undef JSON_HEDLEY_ARM_VERSION_CHECK
@@ -24549,6 +26016,7 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
#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
@@ -24592,12 +26060,16 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
#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
@@ -24662,4 +26134,4 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
-#endif // INCLUDE_NLOHMANN_JSON_HPP_
+#endif // INCLUDE_NLOHMANN_JSON_HPP_ \ No newline at end of file