initial import, >900commits predate this

This commit is contained in:
Green Sky 2020-09-29 13:47:50 +02:00
commit e74154ccee
352 changed files with 108120 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
.vs/
*.o
*.swp
~*
*~
.idea/
cmake-build-debug/
cmake-build-debugandtest/
cmake-build-release/
*.stackdump
*.coredump
compile_commands.json
/build
.clangd
.DS_Store
.AppleDouble
.LSOverride
CMakeLists.txt.user*
CMakeCache.txt

39
.gitmodules vendored Normal file
View File

@ -0,0 +1,39 @@
[submodule "external/entt"]
path = external/entt
url = https://github.com/skypjack/entt.git
shallow = true
[submodule "external/glm"]
path = external/glm
url = https://github.com/g-truc/glm.git
shallow = true
[submodule "external/googletest"]
path = external/googletest
url = https://github.com/google/googletest.git
[submodule "external/IconFontCppHeaders"]
path = external/IconFontCppHeaders
url = https://github.com/juliettef/IconFontCppHeaders.git
[submodule "external/imgui/imgui"]
path = external/imgui/imgui
url = https://github.com/ocornut/imgui.git
shallow = true
[submodule "external/ImGuiColorTextEdit"]
path = external/ImGuiColorTextEdit
url = https://github.com/BalazsJako/ImGuiColorTextEdit.git
[submodule "external/physfs"]
path = external/physfs
url = https://github.com/Green-Sky/physfs.git
shallow = true
[submodule "external/soloud/soloud"]
path = external/soloud/soloud
url = https://github.com/jarikomppa/soloud.git
[submodule "external/spdlog"]
path = external/spdlog
url = https://github.com/gabime/spdlog.git
shallow = true
[submodule "external/stb/stb"]
path = external/stb/stb
url = https://github.com/nothings/stb.git
[submodule "external/tracy/tracy"]
path = external/tracy/tracy
url = https://github.com/wolfpld/tracy.git
shallow = true

60
CMakeLists.txt Normal file
View File

@ -0,0 +1,60 @@
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
# cmake setup begin
project(MushMachine C CXX)
# defaulting to debug mode, if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
endif()
# setup my vim ycm :D
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# paths
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# more paths
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
include("${CMAKE_CURRENT_LIST_DIR}/mm_options_and_defines.cmake")
# external libs
add_subdirectory("external") # before increasing warn levels (sad :( )
set(CMAKE_CXX_EXTENSIONS OFF)
# bump up warning levels appropriately for clang, gcc & msvc
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
add_compile_options(
-Wall -Wextra # Reasonable and standard
-Wpedantic # Warn if non-standard C++ is used
-Wunused # Warn on anything being unused
-Wconversion # Warn on type conversions that may lose data
-Wsign-conversion # Warn on sign conversions
-Wshadow # Warn if a variable declaration shadows one from a parent context
)
#link_libraries(-fsanitize=address,undefined)
#link_libraries(-fsanitize=undefined)
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
endif()
if (BUILD_TESTING)
enable_testing()
endif()
# cmake setup end
# MM code
add_subdirectory("framework")
add_subdirectory("systems")
add_subdirectory("screens")

25
LICENSE Normal file
View File

@ -0,0 +1,25 @@
The Code is under the following License, if not stated otherwise:
MIT License
Copyright (c) 2018-2019 Felix Richter
Copyright (c) 2018-2020 Erik Scholz
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.

69
README.md Normal file
View File

@ -0,0 +1,69 @@
![mm](res/mm_card.png)
# MushMachine
This is a Game Engine/Framework, originally developed for GH**deducted** (a 2D game), but has since expanded its scope to be more generic. For example it also includes 3D stuff, instead of just 2D. Furthermore it got stripped of some dependency heavy dependencies (yea that's a thing).
The initial Developers are Erik Scholz and Felix Richter.
Currently developed and maintained by Erik Scholz.
The private repo had over 900 commits, before the first public release.
## Links
- [Gitlab](https://gitlab.com/MadeOfJelly/MushMachine)
- [Github](https://github.com/MadeOfJelly/MushMachine)
- [Setup](docs/setup.md)
- [Example Game Setup](docs/basic_game.md)
- The [Docs Directory](docs/) contains Documentation loosly based on the Source structure.
## Platforms
- PC (Linux, Windows and maybe APPLE(untested))
- Android (untested)
- Web (huh? yea! via Emscripten, wasm and WebGL2)
- anything if you put your mind to it....
- Rendering wise, only Opengl3.3/ES3 is included in the Engine
## Roadmap
- Rewrite the Input service (the code was ported in and is older than the Engine (2017))
- Extend the SDL service with Vulkan support (it's 2020 so yea)
- Filesystem refactor (like a proper file type, with destructor and stuff)
- l10n support
- ImGui integration refactor (for making it usable as a game ui, not just for debugging)
- more service interfaces (with focus on networking)
- multithreading updates (maybe a jobsystem idk)
## Limitations
- Features in general. It's more of a Personal Framework than something I want everyone to use ;).
- Only ships where OpenGL 3.3 or OpenGL ES 3 are available (includes web via emsripten's ES3 emu)
- not optimized for performance
- no multithreading in the Engine itself
- basically most of the Roadmap fits here too
## License
MIT you bet
## Source Structure
## Libraries Used
- [EnTT](https://github.com/skypjack/entt)
- [glad](https://github.com/Dav1dde/glad)
- [GLM](https://github.com/g-truc/glm)
- [googletest](https://github.com/google/googletest)
- [IconFontCppHeaders](https://github.com/juliettef/IconFontCppHeaders)
- [Dear ImGui](https://github.com/ocornut/imgui)
- [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit)
- [nlohmann::json](https://github.com/nlohmann/json)
- [OpenGLSky](https://github.com/shff/opengl_sky) (fast_sky)
- [PhysFS](https://icculus.org/physfs/)
- [SDL2](https://www.libsdl.org/)
- [SoLoud](https://sol.gfxile.net/soloud/)
- [spdlog](https://github.com/gabime/spdlog)
- [stb](https://github.com/nothings/stb)
- [Tracy](https://github.com/wolfpld/tracy)
- [Emscripten](https://emscripten.org/)
- others I might have forgotten... should be cedited in the code. (make a issue/pr if a credit is missing)
## Thanks
- The GH**deducted**-Team, the reason this exists.
- ...and to all the developers of the libraries used in this project. <3

291
cmake/FindSDL2.cmake Normal file
View File

@ -0,0 +1,291 @@
# Locate SDL2 library
# This module defines
# SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2_main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARY variable.
#
# Don't forget to include SDL2main.h and SDL2main.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL2_LIBRARY
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
#
#
# $SDL2 is an environment variable that would
# correspond to the ./configure --prefix=$SDL2
# used in building SDL2.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL2 guidelines.
# Added a search for SDL2main which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL2_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
# This needed to change because "proper" SDL2 convention
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL2/ (see FreeBSD).
#
# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake
# module with the minor edit of changing "SDL" to "SDL2" where necessary. This
# was not created for redistribution, and exists temporarily pending official
# SDL2 CMake modules.
#
# Note that on windows this will only search for the 32bit libraries, to search
# for 64bit change x86/i686-w64 to x64/x86_64-w64
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# CMake - Cross Platform Makefile Generator
# Copyright 2000-2014 Kitware, Inc.
# Copyright 2000-2011 Insight Software Consortium
# 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 names of Kitware, Inc., the Insight Software Consortium,
# nor the names of their contributors may be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
if(NOT EMSCRIPTEN)
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
HINTS
$ENV{SDL2}
PATH_SUFFIXES include/SDL2 include SDL2
i686-w64-mingw32/include/SDL2
x86_64-w64-mingw32/include/SDL2
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local/include/SDL2
/usr/include/SDL2
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
# Lookup the 64 bit libs on x64
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
HINTS
$ENV{SDL2}
PATH_SUFFIXES lib64 lib
lib/x64
x86_64-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
# On 32bit build find the 32bit libs
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
HINTS
$ENV{SDL2}
PATH_SUFFIXES lib
lib/x86
i686-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
IF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
# Lookup the 64 bit libs on x64
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2}
PATH_SUFFIXES lib64 lib
lib/x64
x86_64-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
# On 32bit build find the 32bit libs
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2}
PATH_SUFFIXES lib
lib/x86
i686-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# SDL2 may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
SET(SDL2_FOUND "NO")
IF(SDL2_LIBRARY_TEMP)
# For SDL2main
IF(NOT SDL2_BUILDING_LIBRARY)
IF(SDL2MAIN_LIBRARY)
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(SDL2MAIN_LIBRARY)
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
############add_library(SDL UNKNOWN IMPORTED)
#target_link_libraries(SDL INTERFACE ${SDL2_LIBRARY})
#target_include_directories(SDL INTERFACE ${SDL2_INCLUDE_DIR})
#set_target_properties(SDL PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES "${SDL2_LIBRARY}")
#set_target_properties(SDL PROPERTIES
#INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}")
#set_target_properties(SDL PROPERTIES
#IMPORTED_LINK_INTERFACE_LANGUAGES "C"
#IMPORTED_LINK_INTERFACE_LIBRARIES "${SDL2_LIBRARY}"
#LINK_INTERFACE_LIBRARIES "${SDL2_LIBRARY}")
#IMPORTED_LOCATION "${SDL2_LIBRARY}")
SET(SDL2_FOUND "YES")
ENDIF(SDL2_LIBRARY_TEMP)
else() #emsripten
set(SDL2_INCLUDE_DIR "./") # hack
SET(SDL2_LIBRARY "-s USE_SDL=2 -s USE_WEBGL2=1" CACHE STRING "Where the SDL2 Library can be found")
add_library(SDL UNKNOWN IMPORTED)
#set_target_properties(SDL PROPERTIES INTERFACE_COMPILE_DEFINITIONS "-s USE_SDL=2")
#set_target_properties(SDL PROPERTIES COMPILE_FLAGS "-s USE_SDL=2 -s USE_WEBGL2=1")
#set_target_properties(SDL PROPERTIES LINK_FLAGS "-s USE_SDL=2 -s USE_WEBGL2=1")
# HACK
set_target_properties(SDL PROPERTIES INTERFACE_COMPILE_DEFINITIONS "TMP -s USE_SDL=2")
#set_target_properties(SDL PROPERTIES INTERFACE_COMPILE_DEFINITIONS "TMP2 -s USE_SDL=2")
#set_target_properties(SDL PROPERTIES INTERFACE_COMPILE_DEFINITIONS "TMP2 -s USE_SDL=2 -s USE_WEBGL2=1")
#set_target_properties(SDL PROPERTIES INTERFACE_LINK_OPTIONS "-s USE_SDL=2 -s USE_WEBGL2=1")
#target_link_libraries(SDL ${SDL2_LIBRARY})
#target_include_directories(SDL ${SDL2_INCLUDE_DIR})
SET(SDL2_FOUND "YES")
endif(NOT EMSCRIPTEN)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)

96
docs/basic_game.md Normal file
View File

@ -0,0 +1,96 @@
# Basic Engine Usage
## General Headers
```c++
#include <mm/engine.hpp>
// or
#include <mm/engine_fwd.hpp> // forward header
```
# Engine Example Setup
```c++
#include <mm/engine.hpp>
// Services you want to use:
// I recommend to do this in a seperate translation unit, for good mesure.
#include <mm/services/sdl_service.hpp>
#include <mm/services/filesystem.hpp> // requires SDL2 if not headless.
#include <mm/services/opengl_renderer.hpp> // requires sdl_service.
#include <mm/services/imgui_s.hpp> // every one <3 DearImGui!
#include <mm/services/simple_scene.hpp> // a simple ECS using EnTT.
#include <mm/services/sound_service.hpp> // SoLoud for Sound!
#include <mm/services/screen_director_simple.hpp> // you want this, go to screen director doc for more info.
MM::Engine engine; // at global scope, only required for emscripten.
// adds Screens, definition at EOF.
void add_screens(MM::Engine& engine, MM::Services::ScreenDirectorSimple& sds);
// argc, argv required for sdl2 on windows.
int main(int argc, char** argv) {
{ // setup ALL Services ever used during runtime.
// 1. setup SDL
// this adds the SDLService to the engine, also returns a ref to the Service.
auto& sdl_ss = engine.addService<MM::Services::SDLService>(
SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER
); // emscripten does not like haptic ...
// this Enables the Service (calles service.enable()), THIS also returns if the enable was successfull!!, for simplicity omitted...
engine.enableService<MM::Services::SDLService>();
// this creates a OpenGL(3.3) ready Window.
sdl_ss.createGLWindow("My MM Game", 1280, 720);
// set icon here if needed.
// 2. filesystem, can be 1. too.
// the Construction of the fs Service takes in to arguments:
// - the path to the executable, it should be the first string in argv, but can be left empty.
// - the name of the application, of sorts. it is used for the folder to write into, so saves and configs edited by the player get written to this directory. it is not located relative to the exe/install.
engine.addService<MM::Services::FilesystemService>(argv[0], "my_mm_game");
// and immediatly enable.
engine.enableService<MM::Services::FilesystemService>();
// 3. add the the remaining Services, which get enabled, disabled during runtime, by eg. ScreenDirector.
engine.addService<MM::Services::ImGuiService>();
engine.addService<MM::Services::SimpleSceneService>();
engine.addService<MM::Services::SoundService>(); // we also could just enable this at setup
engine.addService<MM::Services::OpenGLRenderer>();
// 4. and not add a ScreenDirector, which manages Screens, which enable/disable a set of Services.
auto& sds = engine.addService<MM::Services::ScreenDirectorSimple>();
// add/setup screens, i really recommend doing this somewhere else.
add_screens(engine, sds);
// also queue the first Screen to be enabled.
sds.queued_screen_id = "MM::Screens::mm_logo";
// and enable, this also switches to the queued screen, if there is one.
engine.enableService<MM::Services::ScreenDirectorSimple>();
}
// run Engine
engine.run(); // blocks
// you can also call engine.update() and engine.fixedUpdate() by hand.
// calling engine.stop() from anywhere will make engine.run() return.
engine.cleanup(); // required for global scope engine. don't do this for emscripten, bc emsripten will leave main() on startup, since you can't have blocking JavaSript...
return 0;
}
void add_screens(MM::Engine& engine, MM::Services::ScreenDirectorSimple& sds) {
// add built-in MM-Logo-Splash-Screen
MM::Screens::create_mm_logo(
engine,
sds.screens["MM::Screens::mm_logo"],
"MM::Screens::mm_logo", // mm_logo specific: a Screen to play after mm_logo has finished. (we loop in this example)
);
// add additional Screens here.
}
```

View File

@ -0,0 +1,36 @@
(draft)
# Common Components
This is a collection of commonly used (ECS) `Components`.
Json serialization is provided for all `Components`.
## Name
Contains a string. For debugging.
It's special, since the entity widget will try to get the Name and display it in `ImGui`.
## Transform
Contians Positon, Rotation and Scale.
2D and 3D variants.
Might get replaced with a Unity3D style transform system...
## Velocity
Contians Velocity(Position and Rotation).
2D and 3D variants.
## View_Dir
In almost every Project I had the need for this `Component`, so it's here.
2D and 3D variants.
## Color
I use this a lot. I probably shouldn't.
Hase 4 color channels.

View File

@ -0,0 +1,75 @@
# Engine
The Engine is the heart of the MushMachine framework.
It holds the Storage of all [`MM::Service`](services.md)s, update callbacks and is responsible for calling them.
# Usage
Provides a forwarding header `<mm/engine_fwd.hpp>`.
## Construction
The `MM::Engine` provides a Constructor which accepts a time interval, in which `.fixedUpdate()` is called. The Default is 1/60.
## Starting
```cpp
#include <mm/engine.hpp>
MM::Engine engine;
// add and enable MM::Services here
engine.run();
```
The `MM::Engine` disables all enabled `MM::Service`s in reverse order on destruction or `.cleanup()`.
## Managing [`MM::Service`](services.md)s
### Adding a `MM::Service`
Before a `MM::Service` can be enabled, it needs to be added to the engine.
`.addService<MyService>(my, args)` adds a service and forwards args to its constructor.
There is currently no way of removing a `MM::Service`.
### Getting a `MM::Service`
You can get a Pointer via `.tryService<MyService>()` and an asserted reference via `.getService<MyService>()`. If the `MM::Service` does not exist, it returns a `nullptr. "Enablement Status" makes no difference.
### Provide a `MM::Service` (Interfaces)
`.provide<MyServiceInterface, MyService>()` registers `MyService` also as `MyServiceInterface`, so `MyService` becomes getable via `MyServiceInterface`. `MyService` needs to be added beforehand.
### Enabling/Disabling a `MM::Service`
`MM::Service`s can be Enabled/Disabled during `.run()` as a deferred operation (see `.addFixedDefer(...)`)
* `.enableService<MyService>()` calles the `.enable()` of `MyService`. Also returns `true` on success.
* `.disableService<MyService>()` calles the `.disable()` of `MyService`.
There are also overloads, which accept a type id.
## Update / FixedUpdate
FixedUpdates are called with update interval (default 1/60, set on engine ctr), while Updates are called "as fast as possible". Since Rendering is usually done using Update and blocks on VSync, this is as fast as VSync.
Since fixedUpdate callbacks don't get a time delta, querying `.getFixedDeltaTime()` is mandatory.
### adding Update callbacks
`.addUpdate(MyFn)` and `.addFixedUpdate(MyFn)` add `MyFn` as an update and fixedUpdate respectively and return a `FunctionDataHandle`(ptr) to remove them again.
### removing Update callbacks
`.removeUpdate(myHandle)` and `.removeFixedUpdate(myHandle)` remove those callbacks again.
### `.run()`
`.run()` is a blocking call, which calles `.fixedUpdate()` in a fixed interval with an accumulator. Inbetween those `.fixedUpdate()`s, `.update()` gets called "as fast as possible".
You don't need to call `.run()`, instead you can call `.update()` and `.fixedUpdate()` yourself.
### `.stop()`
Stops the Engine...
Actually quits the loop in `.run()`.

View File

@ -0,0 +1,23 @@
# Default Shipped Interfaces
### Index
* `MM::Services::SceneServiceInterface`
## SceneServiceInterface
Not very stable. But I use it all over the place.
It uses [`EnTT`](https://github.com/skypjack/entt).
### interface
Use `.getScene()` to get current `MM::Scene`.
Use `.changeScene(newScene)` to queue `.changeSceneNow(newScene)`.
Use `.changeSceneNow(newScene)` to change currently held `MM::Scene`.
Use `.addSystemToScene(fn)` to add a `MM::System` to the `MM::Scene`.

View File

@ -0,0 +1,18 @@
# Services
`MM::Service`s are the Components that get added to the `MM::Engine`.
They need to extend `MM::Service` (included in `<mm/engine.hpp>`) and implement the following Interface:
* Destructor (since it is virtual)
* `bool enable(MM::Engine& engine)`
* `void disable(MM::Engine& engine)`
and optionally
* `const char* name(void)`
Not implementing `name()` does not impact functionality, but it is not good practise.
Update and FixedUpdate callbacks should only be added in `enable()` and should be removed in `disable()`.

View File

@ -0,0 +1,7 @@
(draft)
# Filesystem
Provides a Virtual Filesystem. Implemented using [PhysFS](https://icculus.org/physfs/).
# Usage

View File

@ -0,0 +1,33 @@
# Logger
The Logger is the [`spdlog`](https://github.com/gabime/spdlog) logging library, with some extras. Like a `Sink` for [`Tracy`](https://github.com/wolfpld/tracy) the Profiler.
# Usage
Each "Section" is ment to have its own "Logger".
To Initialize a section logger with its `sink`s, do `MM::Logger::initSectionLogger("MySection");`.
Can be placed in the `.enable()` or even the Constructor of your `Service`s.
```cpp
#include <mm/logger.hpp> // include logger
// here are the macros
#define LOG_CRIT(...) __LOG_CRIT( "MySection", __VA_ARGS__)
#define LOG_ERROR(...) __LOG_ERROR("MySection", __VA_ARGS__)
#define LOG_WARN(...) __LOG_WARN( "MySection", __VA_ARGS__)
#define LOG_INFO(...) __LOG_INFO( "MySection", __VA_ARGS__)
#define LOG_DEBUG(...) __LOG_DEBUG("MySection", __VA_ARGS__)
#define LOG_TRACE(...) __LOG_TRACE("MySection", __VA_ARGS__)
// code goes here ......
// only if in header
//#undef LOG_CRIT
//#undef LOG_ERROR
//#undef LOG_WARN
//#undef LOG_INFO
//#undef LOG_DEBUG
//#undef LOG_TRACE
```

View File

@ -0,0 +1,5 @@
(draft)
## OpenGL Primitives
Mostly Classes wrapping the OpenGL library.

View File

@ -0,0 +1,7 @@
(draft)
# OpenGL Renderer
This is a "Renderer" implementation. It hosts a `OpenGL::RenderTask` list, which get executed in order.
For `OpenGL::RenderTask`s see the [render_tasks folder](render_tasks/).

View File

@ -0,0 +1,6 @@
# `OpenGL::RenderTask` Blit FB
This `RenderTask` Blits(copys) the contents of one `FrameBuffer` to another.
There are options available for source and destination rectangle, color flags and filter mode.

View File

@ -0,0 +1,6 @@
# `OpenGL::RenderTask` Clear
This `RenderTask` calles glClear().
There are options available for clear color and color mask.

View File

@ -0,0 +1,4 @@
# `OpenGL::RenderTask` ImGuiRT
This `RenderTask` renders the ImGui render data.

View File

@ -0,0 +1,7 @@
(draft)
# Resource Manager
This is nothing more than a glorified Singelton and will be replace in the future.
Based on https://github.com/skypjack/entt/blob/master/src/entt/resource/cache.hpp.

View File

@ -0,0 +1,31 @@
# Screen Director
Orchistrates `Screen`s.
The `Screen Director` lets you define "States"(`Screen`s) which activate/deactivate `MM::Service`s on Entering and Leaving the `Screen`.
# Usage
The current implementation is very open and does not really use encapsulation ...
## Adding `Screen`s
Due to the open nature of this implementation, one does access members directly:
```
auto& my_screen = screen_director.screens["my_screen"];
// filling in start_disable, start_enable, start_provide, end_disable and end_enable
// and optionally start_fn and end_fn which are called upon entering and leaving the screen.
my_screen.start_enable.push_back(engine.type<MyService>());
// [...]
```
## Changing to a Screen
Changing to a `Screen` can either be done on call with `.changeScreenTo(engine, "my_screen_id")` or qued via `.queueChangeScreenTo("my_screen")`. The former should only be used when the `Engine` is not running or in a `fixedDeferUpdate`.
## Service Enablement
Enabling the ScreenDirector causes a queued `Screen` to be started.

View File

@ -0,0 +1,9 @@
(draft)
## SDL Service
This is a `Service` which manages Windows, OpenGL Contexts and Events.
The current implementation limits it to one Window and OpenGL Context.
Support for Vulkan is planned.

View File

@ -0,0 +1,5 @@
(draft)
# Simple Scene
This is a simple `MM::SceneServiceInterface` implementation.

68
docs/setup.md Normal file
View File

@ -0,0 +1,68 @@
# Setting up MushMachine
## Game development
First add MM as a git submodule if in git repo:
```bash
git submodule add --init --recursive <url>
```
else just git clone
add to cmake as subdirectory
```cmake
add_subdirectory(MushMachine)
```
The Engine is intended to be linked statically, but you can explore dynamic-linking of course.
## Engine development
* clone repo
* install dependencies
### GNU/LiNUX
* install dependencies
- libsdl2
* navigate to (out of tree) build directory (prefer just "build" in repo-dir)
* `cmake ..`
### OSX
never tested before. be the first one :) .
### Windows
* install dependencies
- using install vcpkg
- install SDL2 (non static)
- or by hand
- download libSDL2 and put it somewere, and set the "SDL2" environment variable to that location (set SDL2="path/to/folder")
#### Visual Studio (GUI)
* open cmake project with visual studio (vs2019; vs2017 may work)
- if using vcpkg and vs2017: set vcpkg cmake toolchain (vs2019 does this automaticly)
#### Visual Studio (MSVC dev cmd)
* open dev cmd (search start menu)
* navigate to (out of tree) build directory (prefer just "build" in repo-dir)
- if using vcpkg
- `cmake .. -DCMAKE_TOOLCHAIN_FILE="<path-to-vcpkg-install>\scripts\buildsystems\vcpkg.cmake" -G Ninja`
- else
- `cmake .. -G Ninja`
#### CLion
TODO
(look at cmake in this doc)
## Additional Notes
### CMake
* to specify build type on console use `-DCMAKE_BUILD_TYPE=<build-type>` eg. `Release`. the engine cmake defaults to `Debug` if not set.
* the engine has cmake-level defines that can be set (via cmd eg. `-DMM_HEADLESS=ON`):
- `MM_HEADLESS` (disables sdl2 and opengl usage)
- `MM_NETWORKING` (depricated)
- `MM_OPENGL_3` (disabled if `MM_HEADLESS`)
- `MM_OPENGL_3_GLES` (disabled if `MM_HEADLESS`, `MM_OPENGL_3` supplement, makes the openglrenderer use gles3 instead of gl3)
### Emscripten
* version 2.0.1 works
* version 1.39.18 does not (webgl errors)

20
docs/terminology.md Normal file
View File

@ -0,0 +1,20 @@
# Terminology
### Engine
is defined as [`MM::Engine`](framework/engine/engine.md).
### Service
is defined as [`MM::Services::Service`](framework/engine/services.md), the Service's base class, which was previously known as `ServiceSystem` and `SubSystem`.
### Scene
is defined as a ECS-style EnTT::registry. It contains Entities, Components and Systems.
### Entity
is defined as a ECS Entity, see [`EnTT`](https://github.com/skypjack/entt).
### System
is defined as a ECS-style System. In this case, it's a free function.
### Screen
is defined as a State of enabled and disabled `Service`s. (see [`ScreenDirector`](framework/screen_director/screen_director.md))

51
external/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
# external libs
if (BUILD_TESTING)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory("googletest")
endif()
add_subdirectory("spdlog")
#include("${CMAKE_CURRENT_SOURCE_DIR}/tracy.cmake")
add_subdirectory("tracy")
include("${CMAKE_CURRENT_SOURCE_DIR}/entt.cmake")
include("${CMAKE_CURRENT_SOURCE_DIR}/glm.cmake")
set(JSON_BuildTests OFF CACHE INTERNAL "")
set(JSON_MultipleHeaders ON CACHE INTERNAL "")
add_subdirectory("json") # link with "nlohmann_json::nlohmann_json"
set(PHYSFS_BUILD_SHARED FALSE CACHE INTERNAL "")
add_subdirectory("physfs")
if(NOT MM_HEADLESS)
if(NOT EMSCRIPTEN)
find_package(SDL2 REQUIRED)
endif()
if(NOT MM_OPENGL_3_GLES)
# opengl3 loader
add_subdirectory("glad-debug")
endif()
# stb utilies
add_subdirectory("stb")
# nice symbol font integration
include("${CMAKE_CURRENT_SOURCE_DIR}/icon_font_cpp_headers.cmake")
# (debug) gui
add_subdirectory("imgui")
# (debug) text editor
include("${CMAKE_CURRENT_SOURCE_DIR}/ImGuiColorTextEdit.cmake")
# sound, uses sdl2-static backend
add_subdirectory("soloud")
endif()

1
external/IconFontCppHeaders vendored Submodule

@ -0,0 +1 @@
Subproject commit bd33e5b7b67509e0f736f01bc03889f9ac8c32f5

1
external/ImGuiColorTextEdit vendored Submodule

@ -0,0 +1 @@
Subproject commit 0a88824f7de8d0bd11d8419066caa7d3469395c4

11
external/ImGuiColorTextEdit.cmake vendored Normal file
View File

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
add_library(imgui_color_text_edit
"${CMAKE_CURRENT_LIST_DIR}/ImGuiColorTextEdit/TextEditor.h"
"${CMAKE_CURRENT_LIST_DIR}/ImGuiColorTextEdit/TextEditor.cpp"
)
target_include_directories(imgui_color_text_edit PUBLIC "${CMAKE_CURRENT_LIST_DIR}/ImGuiColorTextEdit")
target_link_libraries(imgui_color_text_edit
imgui
)

1
external/entt vendored Submodule

@ -0,0 +1 @@
Subproject commit bf0160adbd8fb405d758808f879de917e81465ca

5
external/entt.cmake vendored Normal file
View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
add_library(entt INTERFACE)
target_include_directories(entt INTERFACE "${CMAKE_CURRENT_LIST_DIR}/entt/src")
target_compile_features(entt INTERFACE cxx_std_17)

22
external/glad-debug/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.2)
project(glad C)
set(C_FILES
"src/glad.c"
)
set(H_FILES
"include/glad/glad.h"
"include/KHR/khrplatform.h"
)
add_library(glad ${C_FILES} ${H_FILES})
target_include_directories(glad PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
if(UNIX)
target_link_libraries(glad
dl
)
endif()

View File

@ -0,0 +1,290 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are 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 Materials.
**
** THE MATERIALS ARE 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
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

2509
external/glad-debug/include/glad/glad.h vendored Normal file

File diff suppressed because it is too large Load Diff

3814
external/glad-debug/src/glad.c vendored Normal file

File diff suppressed because it is too large Load Diff

1
external/glm vendored Submodule

@ -0,0 +1 @@
Subproject commit bf71a834948186f4097caa076cd2663c69a10e1e

5
external/glm.cmake vendored Normal file
View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
add_library(glm INTERFACE)
target_include_directories(glm INTERFACE "${CMAKE_CURRENT_LIST_DIR}/glm")

1
external/googletest vendored Submodule

@ -0,0 +1 @@
Subproject commit 705b9c1af24953bbbe757f192fa25c44a9e92e7c

4
external/icon_font_cpp_headers.cmake vendored Normal file
View File

@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
add_library(icon_font_cpp_headers INTERFACE)
target_include_directories(icon_font_cpp_headers INTERFACE "${CMAKE_CURRENT_LIST_DIR}/IconFontCppHeaders/")

60
external/imgui/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,60 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
project(imgui C CXX)
set(CPP_FILES
"imgui/imgui.cpp"
"imgui/imgui_draw.cpp"
"imgui/imgui_widgets.cpp"
"imgui/imgui_demo.cpp"
"imgui/misc/cpp/imgui_stdlib.cpp"
"imgui_plot_var.cpp"
#"imgui_impl_sdl_gl3.cpp"
"imgui_impl_sdl.cpp"
"imgui_impl_opengl3.cpp"
)
set(HPP_FILES
"imgui/imgui.h"
"imgui/imstb_rectpack.h"
"imgui/imstb_textedit.h"
"imgui/imstb_truetype.h"
"imgui/misc/cpp/imgui_stdlib.h"
"imgui_plot_var.hpp"
#"imgui_impl_sdl_gl3.h"
"imgui_impl_sdl.h"
"imgui_impl_opengl3.h"
)
add_library(imgui ${CPP_FILES} ${HPP_FILES})
if(MM_OPENGL_3_GLES)
target_compile_definitions(imgui PUBLIC IMGUI_IMPL_OPENGL_ES3)
target_compile_definitions(imgui PUBLIC IMGUI_IMPL_OPENGL_LOADER_CUSTOM) # bug?
else()
target_compile_definitions(imgui PUBLIC IMGUI_IMPL_OPENGL_LOADER_GLAD)
target_link_libraries(imgui glad)
endif()
target_include_directories(imgui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
#target_include_directories(imgui PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/imgui")
target_include_directories(imgui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/imgui") # im sad
if(EMSCRIPTEN)
set_target_properties(imgui PROPERTIES COMPILE_FLAGS "-s USE_SDL=2 -s USE_WEBGL2=1 -s FULL_ES2=1 -s FULL_ES3=1")
set_target_properties(imgui PROPERTIES LINK_FLAGS "-s USE_SDL=2 -s USE_WEBGL2=1 -s FULL_ES2=1 -s FULL_ES3=1")
elseif(ANDROID)
target_link_libraries(imgui SDL)
else() # if not android or emscripten
target_include_directories(imgui PUBLIC "${SDL2_INCLUDE_DIR}")
target_link_libraries(imgui ${SDL2_LIBRARY})
endif()
target_compile_definitions(imgui PUBLIC ImTextureID=unsigned\ long)

1
external/imgui/imgui vendored Submodule

@ -0,0 +1 @@
Subproject commit 95c99aaa4be611716093edcb6b01146ab9483f30

682
external/imgui/imgui_impl_opengl3.cpp vendored Normal file
View File

@ -0,0 +1,682 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader.
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
//----------------------------------------
// OpenGL GLSL GLSL
// version version string
//----------------------------------------
// 2.0 110 "#version 110"
// 2.1 120 "#version 120"
// 3.0 130 "#version 130"
// 3.1 140 "#version 140"
// 3.2 150 "#version 150"
// 3.3 330 "#version 330 core"
// 4.0 400 "#version 400 core"
// 4.1 410 "#version 410 core"
// 4.2 420 "#version 410 core"
// 4.3 430 "#version 430 core"
// ES 2.0 100 "#version 100" = WebGL 1.0
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
// GL includes
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h>
#elif defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
#else
#include <GLES3/gl3.h> // Use GL ES 3
#endif
#else
// About Desktop OpenGL function loaders:
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
#include <glad/gl.h> // Needs to be initialized with gladLoadGL(...) or gladLoaderLoadGL() in user's code.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
#ifndef GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
#endif
#include <glbinding/Binding.h> // Needs to be initialized with glbinding::Binding::initialize() in user's code.
#include <glbinding/gl/gl.h>
using namespace gl;
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
#ifndef GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
#endif
#include <glbinding/glbinding.h>// Needs to be initialized with glbinding::initialize() in user's code.
#include <glbinding/gl/gl.h>
using namespace gl;
#else
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif
#endif
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) || !defined(GL_VERSION_3_2)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 0
#else
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 1
#endif
// OpenGL Data
static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
static GLuint g_FontTexture = 0;
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
// Query for GL version (e.g. 320 for GL 3.2)
#if !defined(IMGUI_IMPL_OPENGL_ES2)
GLint major, minor;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
g_GlVersion = (GLuint)(major * 100 + minor * 10);
#else
g_GlVersion = 200; // GLES 2
#endif
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 320)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
#if defined(IMGUI_IMPL_OPENGL_ES2)
if (glsl_version == NULL)
glsl_version = "#version 100";
#elif defined(IMGUI_IMPL_OPENGL_ES3)
if (glsl_version == NULL)
glsl_version = "#version 300 es";
#elif defined(__APPLE__)
if (glsl_version == NULL)
glsl_version = "#version 150";
#else
if (glsl_version == NULL)
glsl_version = "#version 130";
#endif
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n");
// Debugging construct to make it easily visible in the IDE and debugger which GL loader has been selected.
// The code actually never uses the 'gl_loader' variable! It is only here so you can read it!
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash below.
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
const char* gl_loader = "Unknown";
IM_UNUSED(gl_loader);
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
gl_loader = "GL3W";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
gl_loader = "GLEW";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
gl_loader = "GLAD";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
gl_loader = "GLAD2";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
gl_loader = "glbinding2";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
gl_loader = "glbinding3";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
gl_loader = "custom";
#else
gl_loader = "none";
#endif
// Make an arbitrary GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
bool clip_origin_lower_left = true;
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&current_clip_origin);
if (current_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
(void)vertex_array_object;
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);
GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);
#ifdef GL_SAMPLER_BINDING
GLuint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler);
#endif
GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
#endif
#ifdef GL_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
#ifndef IMGUI_IMPL_OPENGL_ES2
glGenVertexArrays(1, &vertex_array_object);
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 320)
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
else
#endif
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
}
}
// Destroy the temporary VAO
#ifndef IMGUI_IMPL_OPENGL_ES2
glDeleteVertexArrays(1, &vertex_array_object);
#endif
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, last_sampler);
#endif
glActiveTexture(last_active_texture);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_UNPACK_ROW_LENGTH
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO& io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->TexID = 0;
g_FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
// Parse GLSL version string
int glsl_version = 130;
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision mediump float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = NULL;
const GLchar* fragment_shader = NULL;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
g_ShaderHandle = glCreateProgram();
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
CheckProgram(g_ShaderHandle, "shader program");
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(g_ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array);
#endif
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; }
if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; }
if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); }
if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); }
if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; }
if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; }
if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
}

87
external/imgui/imgui_impl_opengl3.h vendored Normal file
View File

@ -0,0 +1,87 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// About Desktop OpenGL function loaders:
// Modern Desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
// About GLSL version:
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// Specific OpenGL ES versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
// Attempt to auto-detect the default Desktop GL loader based on available header files.
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash in ImGui_ImplOpenGL3_Init().
// You can explicitly select a loader by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
&& !defined(IMGUI_IMPL_OPENGL_ES3) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
// Try to detect GLES on matching platforms
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
#elif defined(__EMSCRIPTEN__)
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
// Otherwise try to detect supported Desktop OpenGL loaders..
#elif defined(__has_include)
#if __has_include(<GL/glew.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
#elif __has_include(<glad/glad.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#elif __has_include(<glad/gl.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD2
#elif __has_include(<GL/gl3w.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#elif __has_include(<glbinding/glbinding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
#elif __has_include(<glbinding/Binding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
#else
#error "Cannot detect OpenGL loader!"
#endif
#else
#define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository
#endif
#endif

370
external/imgui/imgui_impl_sdl.cpp vendored Normal file
View File

@ -0,0 +1,370 @@
// dear imgui: Platform Binding for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// (Requires: SDL 2.0. Prefer SDL 2.0.4+ for full feature support.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends.
// 2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2).
// 2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state).
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
// 2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.
// 2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.
// 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).
// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS.
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#include "imgui_impl_sdl.h"
// SDL
#include <SDL.h>
#include <SDL_syswm.h>
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4)
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
// Data
static SDL_Window* g_Window = NULL;
static Uint64 g_Time = 0;
static bool g_MousePressed[3] = { false, false, false };
static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
static char* g_ClipboardTextData = NULL;
static bool g_MouseCanUseGlobalState = true;
static const char* ImGui_ImplSDL2_GetClipboardText(void*)
{
if (g_ClipboardTextData)
SDL_free(g_ClipboardTextData);
g_ClipboardTextData = SDL_GetClipboardText();
return g_ClipboardTextData;
}
static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
{
SDL_SetClipboardText(text);
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
{
ImGuiIO& io = ImGui::GetIO();
switch (event->type)
{
case SDL_MOUSEWHEEL:
{
if (event->wheel.x > 0) io.MouseWheelH += 1;
if (event->wheel.x < 0) io.MouseWheelH -= 1;
if (event->wheel.y > 0) io.MouseWheel += 1;
if (event->wheel.y < 0) io.MouseWheel -= 1;
return true;
}
case SDL_MOUSEBUTTONDOWN:
{
if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true;
if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true;
if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true;
return true;
}
case SDL_TEXTINPUT:
{
io.AddInputCharactersUTF8(event->text.text);
return true;
}
case SDL_KEYDOWN:
case SDL_KEYUP:
{
int key = event->key.keysym.scancode;
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
#ifdef _WIN32
io.KeySuper = false;
#else
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
#endif
return true;
}
}
return false;
}
static bool ImGui_ImplSDL2_Init(SDL_Window* window)
{
g_Window = window;
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_sdl";
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
io.KeyMap[ImGuiKey_KeyPadEnter] = SDL_SCANCODE_KP_ENTER;
io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
io.ClipboardUserData = NULL;
// Load mouse cursors
g_MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
g_MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
g_MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
g_MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
// Check and store if we are on Wayland
g_MouseCanUseGlobalState = strncmp(SDL_GetCurrentVideoDriver(), "wayland", 7) != 0;
#ifdef _WIN32
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(window, &wmInfo);
io.ImeWindowHandle = wmInfo.info.win.window;
#else
(void)window;
#endif
return true;
}
bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
{
(void)sdl_gl_context; // Viewport branch will need this.
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
{
#if !SDL_HAS_VULKAN
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
{
#if !defined(_WIN32)
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
{
return ImGui_ImplSDL2_Init(window);
}
void ImGui_ImplSDL2_Shutdown()
{
g_Window = NULL;
// Destroy last known clipboard data
if (g_ClipboardTextData)
SDL_free(g_ClipboardTextData);
g_ClipboardTextData = NULL;
// Destroy SDL mouse cursors
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
SDL_FreeCursor(g_MouseCursors[cursor_n]);
memset(g_MouseCursors, 0, sizeof(g_MouseCursors));
}
static void ImGui_ImplSDL2_UpdateMousePosAndButtons()
{
ImGuiIO& io = ImGui::GetIO();
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
SDL_WarpMouseInWindow(g_Window, (int)io.MousePos.x, (int)io.MousePos.y);
else
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
int mx, my;
Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my);
io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false;
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS)
SDL_Window* focused_window = SDL_GetKeyboardFocus();
if (g_Window == focused_window)
{
if (g_MouseCanUseGlobalState)
{
// SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?)
// The creation of a new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally.
// Won't use this workaround when on Wayland, as there is no global mouse position.
int wx, wy;
SDL_GetWindowPosition(focused_window, &wx, &wy);
SDL_GetGlobalMouseState(&mx, &my);
mx -= wx;
my -= wy;
}
io.MousePos = ImVec2((float)mx, (float)my);
}
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger the OS window resize cursor.
// The function is only supported from SDL 2.0.4 (released Jan 2016)
bool any_mouse_button_down = ImGui::IsAnyMouseDown();
SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE);
#else
if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS)
io.MousePos = ImVec2((float)mx, (float)my);
#endif
}
static void ImGui_ImplSDL2_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
SDL_ShowCursor(SDL_FALSE);
}
else
{
// Show OS mouse cursor
SDL_SetCursor(g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]);
SDL_ShowCursor(SDL_TRUE);
}
}
static void ImGui_ImplSDL2_UpdateGamepads()
{
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Get gamepad
SDL_GameController* game_controller = SDL_GameControllerOpen(0);
if (!game_controller)
{
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
return;
}
// Update gamepad inputs
#define MAP_BUTTON(NAV_NO, BUTTON_NO) { io.NavInputs[NAV_NO] = (SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0) ? 1.0f : 0.0f; }
#define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
MAP_BUTTON(ImGuiNavInput_Activate, SDL_CONTROLLER_BUTTON_A); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, SDL_CONTROLLER_BUTTON_B); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, SDL_CONTROLLER_BUTTON_X); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, SDL_CONTROLLER_BUTTON_Y); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768);
MAP_ANALOG(ImGuiNavInput_LStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767);
MAP_ANALOG(ImGuiNavInput_LStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32767);
MAP_ANALOG(ImGuiNavInput_LStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767);
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
#undef MAP_BUTTON
#undef MAP_ANALOG
}
void ImGui_ImplSDL2_NewFrame(SDL_Window* window)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
// Setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
SDL_GetWindowSize(window, &w, &h);
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
w = h = 0;
SDL_GL_GetDrawableSize(window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
static Uint64 frequency = SDL_GetPerformanceFrequency();
Uint64 current_time = SDL_GetPerformanceCounter();
io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f);
g_Time = current_time;
ImGui_ImplSDL2_UpdateMousePosAndButtons();
ImGui_ImplSDL2_UpdateMouseCursor();
// Update game controllers (if enabled and available)
ImGui_ImplSDL2_UpdateGamepads();
}

29
external/imgui/imgui_impl_sdl.h vendored Normal file
View File

@ -0,0 +1,29 @@
// dear imgui: Platform Binding for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
struct SDL_Window;
typedef union SDL_Event SDL_Event;
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window);
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);

72
external/imgui/imgui_plot_var.cpp vendored Normal file
View File

@ -0,0 +1,72 @@
#include "imgui_plot_var.hpp"
#include <map>
#include <algorithm>
struct PlotVarData {
ImGuiID ID;
ImVector<float> Data;
int DataInsertIdx;
int LastFrame;
PlotVarData() : ID(0), DataInsertIdx(0), LastFrame(-1) {}
};
typedef std::map<ImGuiID, PlotVarData> PlotVarsMap;
static PlotVarsMap g_PlotVarsMap;
// Plot value over time
// Call with 'value == FLT_MAX' to draw without adding new value to the buffer
void ImGui::PlotVar(const char* label, float value, float scale_min, float scale_max, int buffer_size)
{
IM_ASSERT(label);
if (buffer_size == 0)
buffer_size = 120;
ImGui::PushID(label);
ImGuiID id = ImGui::GetID("");
// Lookup O(log N)
PlotVarData& pvd = g_PlotVarsMap[id];
// Setup
if (pvd.Data.capacity() != buffer_size)
{
pvd.Data.resize(buffer_size);
memset(&pvd.Data[0], 0, sizeof(float) * buffer_size);
pvd.DataInsertIdx = 0;
pvd.LastFrame = -1;
}
// Insert (avoid unnecessary modulo operator)
if (pvd.DataInsertIdx == buffer_size)
pvd.DataInsertIdx = 0;
int display_idx = pvd.DataInsertIdx;
if (value != FLT_MAX)
pvd.Data[pvd.DataInsertIdx++] = value;
// Draw
int current_frame = ImGui::GetFrameCount();
if (pvd.LastFrame != current_frame)
{
ImGui::PlotLines("##plot", &pvd.Data[0], buffer_size, pvd.DataInsertIdx, NULL, scale_min, scale_max, ImVec2(0, 40));
ImGui::SameLine();
ImGui::Text("%s\n%-3.4f", label, pvd.Data[display_idx]); // Display last value in buffer
pvd.LastFrame = current_frame;
}
ImGui::PopID();
}
void ImGui::PlotVarFlushOldEntries()
{
int current_frame = ImGui::GetFrameCount();
for (PlotVarsMap::iterator it = g_PlotVarsMap.begin(); it != g_PlotVarsMap.end(); )
{
PlotVarData& pvd = it->second;
if (pvd.LastFrame < current_frame - std::max(400,(int)pvd.Data.size()))
it = g_PlotVarsMap.erase(it);
else
++it;
}
}

19
external/imgui/imgui_plot_var.hpp vendored Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include "imgui/imgui.h"
// USAGE
// Call once a frame with current value
// ImGui::PlotVar("Speed", current_speed);
namespace ImGui {
// Plot value over time
// Pass FLT_MAX value to draw without adding a new value
void PlotVar(const char* label, float value, float scale_min = FLT_MAX, float scale_max = FLT_MAX, int buffer_size = 120);
// Call this periodically to discard old/unused data
void PlotVarFlushOldEntries();
}

41
external/json/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 3.1)
##
## PROJECT
## name and version
##
project(nlohmann_json VERSION 3.8.0 LANGUAGES CXX)
##
## OPTIONS
##
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
##
## CONFIGURATION
##
set(NLOHMANN_JSON_TARGET_NAME ${PROJECT_NAME})
if (JSON_MultipleHeaders)
set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/include/")
message(STATUS "Using the multi-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}")
else()
set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/single_include/")
message(STATUS "Using the single-header code from ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}")
endif()
##
## TARGET
## create target and add include path
##
add_library(${NLOHMANN_JSON_TARGET_NAME} INTERFACE)
add_library(${PROJECT_NAME}::${NLOHMANN_JSON_TARGET_NAME} ALIAS ${NLOHMANN_JSON_TARGET_NAME})
if (${CMAKE_VERSION} VERSION_LESS "3.8.0")
target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_range_for)
else()
target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_std_11)
endif()
target_include_directories(${NLOHMANN_JSON_TARGET_NAME} INTERFACE ${NLOHMANN_JSON_INCLUDE_BUILD_DIR})

View File

@ -0,0 +1,49 @@
#pragma once
#include <utility>
#include <nlohmann/detail/conversions/from_json.hpp>
#include <nlohmann/detail/conversions/to_json.hpp>
namespace nlohmann
{
template<typename, typename>
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).
@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(
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
{
::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
}
/*!
@brief convert any value type to a JSON value
This function is usually called by the constructors of the @ref basic_json
class.
@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())
{
::nlohmann::to_json(j, std::forward<ValueType>(val));
}
};
} // namespace nlohmann

View File

@ -0,0 +1,166 @@
#pragma once
#include <cstdint> // uint8_t
#include <tuple> // tie
#include <utility> // move
namespace nlohmann
{
/*!
@brief an internal type for a backed binary type
This type extends the template parameter @a BinaryType provided to `basic_json`
with a subtype used by BSON and MessagePack. This type exists so that the user
does not have to specify a type themselves with a specific naming scheme in
order to override the binary type.
@tparam BinaryType container to store bytes (`std::vector<std::uint8_t>` by
default)
@since version 3.8.0
*/
template<typename BinaryType>
class byte_container_with_subtype : public BinaryType
{
public:
/// the type of the underlying container
using container_type = BinaryType;
byte_container_with_subtype() noexcept(noexcept(container_type()))
: container_type()
{}
byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))
: container_type(b)
{}
byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))
: container_type(std::move(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_has_subtype(true)
{}
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_has_subtype(true)
{}
bool operator==(const byte_container_with_subtype& rhs) const
{
return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
}
bool operator!=(const byte_container_with_subtype& rhs) const
{
return !(rhs == *this);
}
/*!
@brief sets the binary subtype
Sets the binary subtype of the value, also flags a binary JSON value as
having a subtype, which has implications for serialization.
@complexity Constant.
@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
subtype
@since version 3.8.0
*/
void set_subtype(std::uint8_t subtype) noexcept
{
m_subtype = subtype;
m_has_subtype = true;
}
/*!
@brief return the binary subtype
Returns the numerical subtype of the value if it has a subtype. If it does
not have a subtype, this function will return size_t(-1) as a sentinel
value.
@return the numerical subtype of the binary value
@complexity Constant.
@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
subtype
@since version 3.8.0
*/
constexpr std::uint8_t subtype() const noexcept
{
return m_subtype;
}
/*!
@brief return whether the value has a subtype
@return whether the value has a subtype
@complexity Constant.
@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
@since version 3.8.0
*/
constexpr bool has_subtype() const noexcept
{
return m_has_subtype;
}
/*!
@brief clears the binary subtype
Clears the binary subtype and flags the value as not having a subtype, which
has implications for serialization; for instance MessagePack will prefer the
bin family over the ext family.
@complexity Constant.
@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
subtype
@since version 3.8.0
*/
void clear_subtype() noexcept
{
m_subtype = 0;
m_has_subtype = false;
}
private:
std::uint8_t m_subtype = 0;
bool m_has_subtype = false;
};
} // namespace nlohmann

View File

@ -0,0 +1,403 @@
#pragma once
#include <algorithm> // transform
#include <array> // array
#include <forward_list> // forward_list
#include <iterator> // inserter, front_inserter, end
#include <map> // map
#include <string> // string
#include <tuple> // tuple, make_tuple
#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
#include <unordered_map> // unordered_map
#include <utility> // pair, declval
#include <valarray> // valarray
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{
JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name())));
}
n = nullptr;
}
// overloads for basic_json template parameters
template < typename BasicJsonType, typename ArithmeticType,
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int > = 0 >
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
{
case value_t::number_unsigned:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
break;
}
case value_t::number_integer:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
break;
}
case value_t::number_float:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
break;
}
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
}
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name())));
}
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
template <
typename BasicJsonType, typename ConstructibleStringType,
enable_if_t <
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(!j.is_string()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
void from_json(const BasicJsonType& j, EnumType& e)
{
typename std::underlying_type<EnumType>::type val;
get_arithmetic_value(j, val);
e = static_cast<EnumType>(val);
}
// forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
l.clear();
std::transform(j.rbegin(), j.rend(),
std::front_inserter(l), [](const BasicJsonType & i)
{
return i.template get<T>();
});
}
// valarray doesn't have an insert method
template<typename BasicJsonType, typename T,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::valarray<T>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l),
[](const BasicJsonType & elem)
{
return elem.template get<T>();
});
}
template<typename BasicJsonType, typename T, std::size_t N>
auto from_json(const BasicJsonType& j, T (&arr)[N])
-> decltype(j.template get<T>(), void())
{
for (std::size_t i = 0; i < N; ++i)
{
arr[i] = j.at(i).template get<T>();
}
}
template<typename BasicJsonType>
void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
{
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
}
template<typename BasicJsonType, typename T, std::size_t N>
auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
priority_tag<2> /*unused*/)
-> decltype(j.template get<T>(), void())
{
for (std::size_t i = 0; i < N; ++i)
{
arr[i] = j.at(i).template get<T>();
}
}
template<typename BasicJsonType, typename ConstructibleArrayType>
auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
-> decltype(
arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
j.template get<typename ConstructibleArrayType::value_type>(),
void())
{
using std::end;
ConstructibleArrayType ret;
ret.reserve(j.size());
std::transform(j.begin(), j.end(),
std::inserter(ret, end(ret)), [](const BasicJsonType & i)
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
return i.template get<typename ConstructibleArrayType::value_type>();
});
arr = std::move(ret);
}
template<typename BasicJsonType, typename ConstructibleArrayType>
void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
priority_tag<0> /*unused*/)
{
using std::end;
ConstructibleArrayType ret;
std::transform(
j.begin(), j.end(), std::inserter(ret, end(ret)),
[](const BasicJsonType & i)
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
return i.template get<typename ConstructibleArrayType::value_type>();
});
arr = std::move(ret);
}
template < typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t <
is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
!is_basic_json<ConstructibleArrayType>::value,
int > = 0 >
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
j.template get<typename ConstructibleArrayType::value_type>(),
void())
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " +
std::string(j.type_name())));
}
from_json_array_impl(j, arr, priority_tag<3> {});
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name())));
}
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
}
template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{
JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
}
ConstructibleObjectType ret;
auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
using value_type = typename ConstructibleObjectType::value_type;
std::transform(
inner_object->begin(), inner_object->end(),
std::inserter(ret, ret.begin()),
[](typename BasicJsonType::object_t::value_type const & p)
{
return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
});
obj = std::move(ret);
}
// overload for arithmetic types, not chosen for basic_json template arguments
// (BooleanType, etc..); note: Is it really necessary to provide explicit
// overloads for boolean_t etc. in case of a custom BooleanType which is not
// an arithmetic type?
template < typename BasicJsonType, typename ArithmeticType,
enable_if_t <
std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int > = 0 >
void from_json(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
{
case value_t::number_unsigned:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
break;
}
case value_t::number_integer:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
break;
}
case value_t::number_float:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
break;
}
case value_t::boolean:
{
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
break;
}
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
}
}
template<typename BasicJsonType, typename A1, typename A2>
void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
{
p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
}
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...> /*unused*/)
{
t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
}
template<typename BasicJsonType, typename... Args>
void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
{
from_json_tuple_impl(j, t, index_sequence_for<Args...> {});
}
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(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
m.clear();
for (const auto& p : j)
{
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name())));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
}
template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
m.clear();
for (const auto& p : j)
{
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name())));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
}
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())
{
return from_json(j, val);
}
};
} // namespace detail
/// 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
{
constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
} // namespace
} // namespace nlohmann

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,374 @@
#pragma once
#include <algorithm> // copy
#include <iterator> // begin, end
#include <string> // string
#include <tuple> // tuple, get
#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
#include <utility> // move, forward, declval, pair
#include <valarray> // valarray
#include <vector> // vector
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
//////////////////
// constructors //
//////////////////
template<value_t> struct external_constructor;
template<>
struct external_constructor<value_t::boolean>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
{
j.m_type = value_t::boolean;
j.m_value = b;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::string>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
{
j.m_type = value_t::string;
j.m_value = s;
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
{
j.m_type = value_t::string;
j.m_value = std::move(s);
j.assert_invariant();
}
template < typename BasicJsonType, typename CompatibleStringType,
enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
int > = 0 >
static void construct(BasicJsonType& j, const CompatibleStringType& str)
{
j.m_type = value_t::string;
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::binary>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
{
j.m_type = value_t::binary;
typename BasicJsonType::binary_t value{b};
j.m_value = value;
j.assert_invariant();
}
template<typename BasicJsonType>
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.assert_invariant();
}
};
template<>
struct external_constructor<value_t::number_float>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
{
j.m_type = value_t::number_float;
j.m_value = val;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::number_unsigned>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
{
j.m_type = value_t::number_unsigned;
j.m_value = val;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::number_integer>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
{
j.m_type = value_t::number_integer;
j.m_value = val;
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::array>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
{
j.m_type = value_t::array;
j.m_value = arr;
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
{
j.m_type = value_t::array;
j.m_value = std::move(arr);
j.assert_invariant();
}
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
int > = 0 >
static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
{
using std::begin;
using std::end;
j.m_type = value_t::array;
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
{
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->reserve(arr.size());
for (const bool x : arr)
{
j.m_value.array->push_back(x);
}
j.assert_invariant();
}
template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
{
j.m_type = value_t::array;
j.m_value = value_t::array;
j.m_value.array->resize(arr.size());
if (arr.size() > 0)
{
std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
}
j.assert_invariant();
}
};
template<>
struct external_constructor<value_t::object>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
{
j.m_type = value_t::object;
j.m_value = obj;
j.assert_invariant();
}
template<typename BasicJsonType>
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{
j.m_type = value_t::object;
j.m_value = std::move(obj);
j.assert_invariant();
}
template < typename BasicJsonType, typename CompatibleObjectType,
enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
{
using std::begin;
using std::end;
j.m_type = value_t::object;
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.assert_invariant();
}
};
/////////////
// to_json //
/////////////
template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
void to_json(BasicJsonType& j, T b) noexcept
{
external_constructor<value_t::boolean>::construct(j, b);
}
template<typename BasicJsonType, typename CompatibleString,
enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
void to_json(BasicJsonType& j, const CompatibleString& s)
{
external_constructor<value_t::string>::construct(j, s);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
{
external_constructor<value_t::string>::construct(j, std::move(s));
}
template<typename BasicJsonType, typename FloatType,
enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
void to_json(BasicJsonType& j, FloatType val) noexcept
{
external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
}
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
{
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
}
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
{
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
}
template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
void to_json(BasicJsonType& j, EnumType e) noexcept
{
using underlying_type = typename std::underlying_type<EnumType>::type;
external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, const std::vector<bool>& e)
{
external_constructor<value_t::array>::construct(j, e);
}
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < is_compatible_array_type<BasicJsonType,
CompatibleArrayType>::value&&
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
!is_basic_json<CompatibleArrayType>::value,
int > = 0 >
void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
{
external_constructor<value_t::array>::construct(j, arr);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
{
external_constructor<value_t::binary>::construct(j, bin);
}
template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
void to_json(BasicJsonType& j, const std::valarray<T>& arr)
{
external_constructor<value_t::array>::construct(j, std::move(arr));
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
{
external_constructor<value_t::array>::construct(j, std::move(arr));
}
template < typename BasicJsonType, typename CompatibleObjectType,
enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
{
external_constructor<value_t::object>::construct(j, obj);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{
external_constructor<value_t::object>::construct(j, std::move(obj));
}
template <
typename BasicJsonType, typename T, std::size_t N,
enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
const T(&)[N]>::value,
int > = 0 >
void to_json(BasicJsonType& j, const T(&arr)[N])
{
external_constructor<value_t::array>::construct(j, arr);
}
template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
{
j = { p.first, p.second };
}
// for https://github.com/nlohmann/json/pull/1134
template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
void to_json(BasicJsonType& j, const T& b)
{
j = { {b.key(), b.value()} };
}
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
{
j = { std::get<Idx>(t)... };
}
template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
void to_json(BasicJsonType& j, const T& t)
{
to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
}
struct to_json_fn
{
template<typename BasicJsonType, typename T>
auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
-> decltype(to_json(j, std::forward<T>(val)), void())
{
return to_json(j, std::forward<T>(val));
}
};
} // namespace detail
/// namespace to hold default `to_json` function
namespace
{
constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
} // namespace
} // namespace nlohmann

View File

@ -0,0 +1,357 @@
#pragma once
#include <exception> // exception
#include <stdexcept> // runtime_error
#include <string> // to_string
#include <nlohmann/detail/input/position_t.hpp>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
////////////////
// exceptions //
////////////////
/*!
@brief general exception of the @ref basic_json class
This class is an extension of `std::exception` objects with a member @a id for
exception ids. It is used as the base class for all exceptions thrown by the
@ref basic_json class. This class can hence be used as "wildcard" to catch
exceptions.
Subclasses:
- @ref parse_error for exceptions indicating a parse error
- @ref invalid_iterator for exceptions indicating errors with iterators
- @ref type_error for exceptions indicating executing a member function with
a wrong type
- @ref out_of_range for exceptions indicating access out of the defined range
- @ref other_error for exceptions indicating other library errors
@internal
@note To have nothrow-copy-constructible exceptions, we internally use
`std::runtime_error` which can cope with arbitrary-length error messages.
Intermediate strings are built with static functions and then passed to
the actual constructor.
@endinternal
@liveexample{The following code shows how arbitrary library exceptions can be
caught.,exception}
@since version 3.0.0
*/
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;
protected:
JSON_HEDLEY_NON_NULL(3)
exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}
static std::string name(const std::string& ename, int id_)
{
return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
}
private:
/// an exception object as storage for error messages
std::runtime_error m;
};
/*!
@brief exception indicating a parse error
This exception is thrown by the library when a parse error occurs. Parse errors
can occur during the deserialization of JSON text, CBOR, MessagePack, as well
as when using JSON Patch.
Member @a byte holds the byte index of the last read character in the input
file.
Exceptions have ids 1xx.
name / id | example message | description
------------------------------ | --------------- | -------------------------
json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.
json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.
json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.
json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.
json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors.
json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.
json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.
json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.
json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.
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
holds true when reading a byte vector (CBOR or MessagePack).
@liveexample{The following code shows how a `parse_error` exception can be
caught.,parse_error}
@sa - @ref exception for the base class of the library exceptions
@sa - @ref invalid_iterator for exceptions indicating errors with iterators
@sa - @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa - @ref out_of_range for exceptions indicating access out of the defined range
@sa - @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class parse_error : public exception
{
public:
/*!
@brief create a parse error exception
@param[in] id_ the id of the exception
@param[in] pos the position where the error occurred (or with
chars_read_total=0 if the position cannot be
determined)
@param[in] what_arg the explanatory string
@return parse_error object
*/
static parse_error create(int id_, const position_t& pos, const std::string& what_arg)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
position_string(pos) + ": " + 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)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
(byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") +
": " + what_arg;
return parse_error(id_, byte_, w.c_str());
}
/*!
@brief byte index of the parse error
The byte index of the last read character in the input file.
@note For an input with n bytes, 1 is the index of the first character and
n+1 is the index of the terminating null byte or the end of file.
This also holds true when reading a byte vector (CBOR or MessagePack).
*/
const std::size_t byte;
private:
parse_error(int id_, std::size_t byte_, const char* what_arg)
: exception(id_, what_arg), byte(byte_) {}
static std::string position_string(const position_t& pos)
{
return " at line " + std::to_string(pos.lines_read + 1) +
", column " + std::to_string(pos.chars_read_current_line);
}
};
/*!
@brief exception indicating errors with iterators
This exception is thrown if iterators passed to a library function do not match
the expected semantics.
Exceptions have ids 2xx.
name / id | example message | description
----------------------------------- | --------------- | -------------------------
json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.
json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.
json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.
json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
@liveexample{The following code shows how an `invalid_iterator` exception can be
caught.,invalid_iterator}
@sa - @ref exception for the base class of the library exceptions
@sa - @ref parse_error for exceptions indicating a parse error
@sa - @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa - @ref out_of_range for exceptions indicating access out of the defined range
@sa - @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class invalid_iterator : public exception
{
public:
static invalid_iterator create(int id_, const std::string& what_arg)
{
std::string w = exception::name("invalid_iterator", id_) + what_arg;
return invalid_iterator(id_, w.c_str());
}
private:
JSON_HEDLEY_NON_NULL(3)
invalid_iterator(int id_, const char* what_arg)
: exception(id_, what_arg) {}
};
/*!
@brief exception indicating executing a member function with a wrong type
This exception is thrown in case of a type error; that is, a library function is
executed on a JSON value whose type does not match the expected semantics.
Exceptions have ids 3xx.
name / id | example message | description
----------------------------- | --------------- | -------------------------
json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.
json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &.
json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.
json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.
json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.
json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.
json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.
json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.
json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.
json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.
json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.
json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.
json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |
@liveexample{The following code shows how a `type_error` exception can be
caught.,type_error}
@sa - @ref exception for the base class of the library exceptions
@sa - @ref parse_error for exceptions indicating a parse error
@sa - @ref invalid_iterator for exceptions indicating errors with iterators
@sa - @ref out_of_range for exceptions indicating access out of the defined range
@sa - @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class type_error : public exception
{
public:
static type_error create(int id_, const std::string& what_arg)
{
std::string w = exception::name("type_error", id_) + what_arg;
return type_error(id_, w.c_str());
}
private:
JSON_HEDLEY_NON_NULL(3)
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
/*!
@brief exception indicating access out of the defined range
This exception is thrown in case a library function is called on an input
parameter that exceeds the expected range, for instance in case of array
indices or nonexisting object keys.
Exceptions have ids 4xx.
name / id | example message | description
------------------------------- | --------------- | -------------------------
json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.
json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.
json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.
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. (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 |
@liveexample{The following code shows how an `out_of_range` exception can be
caught.,out_of_range}
@sa - @ref exception for the base class of the library exceptions
@sa - @ref parse_error for exceptions indicating a parse error
@sa - @ref invalid_iterator for exceptions indicating errors with iterators
@sa - @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa - @ref other_error for exceptions indicating other library errors
@since version 3.0.0
*/
class out_of_range : public exception
{
public:
static out_of_range create(int id_, const std::string& what_arg)
{
std::string w = exception::name("out_of_range", id_) + what_arg;
return out_of_range(id_, w.c_str());
}
private:
JSON_HEDLEY_NON_NULL(3)
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
/*!
@brief exception indicating other library errors
This exception is thrown in case of errors that cannot be classified with the
other exception types.
Exceptions have ids 5xx.
name / id | example message | description
------------------------------ | --------------- | -------------------------
json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
@sa - @ref exception for the base class of the library exceptions
@sa - @ref parse_error for exceptions indicating a parse error
@sa - @ref invalid_iterator for exceptions indicating errors with iterators
@sa - @ref type_error for exceptions indicating executing a member function with
a wrong type
@sa - @ref out_of_range for exceptions indicating access out of the defined range
@liveexample{The following code shows how an `other_error` exception can be
caught.,other_error}
@since version 3.0.0
*/
class other_error : public exception
{
public:
static other_error create(int id_, const std::string& what_arg)
{
std::string w = exception::name("other_error", id_) + what_arg;
return other_error(id_, w.c_str());
}
private:
JSON_HEDLEY_NON_NULL(3)
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,117 @@
#pragma once
#include <cstddef> // size_t, uint8_t
#include <functional> // hash
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 nlohmann::detail::value_t::number_unsigned:
{
const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
return combine(type, h);
}
case nlohmann::detail::value_t::number_float:
{
const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
return combine(type, h);
}
case nlohmann::detail::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); // LCOV_EXCL_LINE
}
}
} // namespace detail
} // namespace nlohmann

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,454 @@
#pragma once
#include <array> // array
#include <cstddef> // size_t
#include <cstdio> //FILE *
#include <cstring> // strlen
#include <istream> // istream
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
#include <memory> // shared_ptr, make_shared, addressof
#include <numeric> // accumulate
#include <string> // string, char_traits
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
#include <utility> // pair, declval
#include <nlohmann/detail/iterators/iterator_traits.hpp>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
/// the supported input formats
enum class input_format_t { json, cbor, msgpack, ubjson, bson };
////////////////////
// input adapters //
////////////////////
/*!
Input adapter for stdio file access. This adapter read only 1 byte and do not use any
buffer. This adapter is a very low level adapter.
*/
class file_input_adapter
{
public:
using char_type = char;
JSON_HEDLEY_NON_NULL(2)
explicit file_input_adapter(std::FILE* f) noexcept
: m_file(f)
{}
// make class move-only
file_input_adapter(const file_input_adapter&) = delete;
file_input_adapter(file_input_adapter&&) = default;
file_input_adapter& operator=(const file_input_adapter&) = delete;
file_input_adapter& operator=(file_input_adapter&&) = delete;
std::char_traits<char>::int_type get_character() noexcept
{
return std::fgetc(m_file);
}
private:
/// the file pointer to read from
std::FILE* m_file;
};
/*!
Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
beginning of input. Does not support changing the underlying std::streambuf
in mid-input. Maintains underlying std::istream and std::streambuf to support
subsequent use of standard std::istream operations to process any input
characters following those used in parsing the JSON input. Clears the
std::istream flags; any input errors (e.g., EOF) will be detected by the first
subsequent call for input from the std::istream.
*/
class input_stream_adapter
{
public:
using char_type = char;
~input_stream_adapter()
{
// clear stream flags; we use underlying streambuf I/O, do not
// maintain ifstream flags, except eof
if (is != nullptr)
{
is->clear(is->rdstate() & std::ios::eofbit);
}
}
explicit input_stream_adapter(std::istream& i)
: is(&i), sb(i.rdbuf())
{}
// delete because of pointer members
input_stream_adapter(const input_stream_adapter&) = delete;
input_stream_adapter& operator=(input_stream_adapter&) = delete;
input_stream_adapter& operator=(input_stream_adapter&& rhs) = delete;
input_stream_adapter(input_stream_adapter&& rhs) noexcept : is(rhs.is), sb(rhs.sb)
{
rhs.is = nullptr;
rhs.sb = nullptr;
}
// std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
// ensure that std::char_traits<char>::eof() and the character 0xFF do not
// end up as the same value, eg. 0xFFFFFFFF.
std::char_traits<char>::int_type get_character()
{
auto res = sb->sbumpc();
// set eof manually, as we don't use the istream interface.
if (JSON_HEDLEY_UNLIKELY(res == EOF))
{
is->clear(is->rdstate() | std::ios::eofbit);
}
return res;
}
private:
/// the associated input stream
std::istream* is = nullptr;
std::streambuf* sb = nullptr;
};
// General-purpose iterator-based adapter. It might not be as fast as
// theoretically possible for some containers, but it is extremely versatile.
template<typename IteratorType>
class iterator_input_adapter
{
public:
using char_type = typename std::iterator_traits<IteratorType>::value_type;
iterator_input_adapter(IteratorType first, IteratorType last)
: current(std::move(first)), end(std::move(last)) {}
typename std::char_traits<char_type>::int_type get_character()
{
if (JSON_HEDLEY_LIKELY(current != end))
{
auto result = std::char_traits<char_type>::to_int_type(*current);
std::advance(current, 1);
return result;
}
else
{
return std::char_traits<char_type>::eof();
}
}
private:
IteratorType current;
IteratorType end;
template<typename BaseInputAdapter, size_t T>
friend struct wide_string_input_helper;
bool empty() const
{
return current == end;
}
};
template<typename BaseInputAdapter, size_t T>
struct wide_string_input_helper;
template<typename BaseInputAdapter>
struct wide_string_input_helper<BaseInputAdapter, 4>
{
// UTF-32
static void fill_buffer(BaseInputAdapter& input,
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
size_t& utf8_bytes_index,
size_t& utf8_bytes_filled)
{
utf8_bytes_index = 0;
if (JSON_HEDLEY_UNLIKELY(input.empty()))
{
utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1;
}
else
{
// get the current character
const auto wc = input.get_character();
// UTF-32 to UTF-8 encoding
if (wc < 0x80)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1;
}
else if (wc <= 0x7FF)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 2;
}
else if (wc <= 0xFFFF)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 3;
}
else if (wc <= 0x10FFFF)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 4;
}
else
{
// unknown character
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1;
}
}
}
};
template<typename BaseInputAdapter>
struct wide_string_input_helper<BaseInputAdapter, 2>
{
// UTF-16
static void fill_buffer(BaseInputAdapter& input,
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
size_t& utf8_bytes_index,
size_t& utf8_bytes_filled)
{
utf8_bytes_index = 0;
if (JSON_HEDLEY_UNLIKELY(input.empty()))
{
utf8_bytes[0] = std::char_traits<char>::eof();
utf8_bytes_filled = 1;
}
else
{
// get the current character
const auto wc = input.get_character();
// UTF-16 to UTF-8 encoding
if (wc < 0x80)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1;
}
else if (wc <= 0x7FF)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 2;
}
else if (0xD800 > wc || wc >= 0xE000)
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
utf8_bytes_filled = 3;
}
else
{
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
{
const auto wc2 = static_cast<unsigned int>(input.get_character());
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
utf8_bytes_filled = 4;
}
else
{
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
utf8_bytes_filled = 1;
}
}
}
}
};
// Wraps another input apdater to convert wide character types into individual bytes.
template<typename BaseInputAdapter, typename WideCharType>
class wide_string_input_adapter
{
public:
using char_type = char;
wide_string_input_adapter(BaseInputAdapter base)
: base_adapter(base) {}
typename std::char_traits<char>::int_type get_character() noexcept
{
// check if buffer needs to be filled
if (utf8_bytes_index == utf8_bytes_filled)
{
fill_buffer<sizeof(WideCharType)>();
JSON_ASSERT(utf8_bytes_filled > 0);
JSON_ASSERT(utf8_bytes_index == 0);
}
// use buffer
JSON_ASSERT(utf8_bytes_filled > 0);
JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
return utf8_bytes[utf8_bytes_index++];
}
private:
BaseInputAdapter base_adapter;
template<size_t T>
void fill_buffer()
{
wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
}
/// a buffer for UTF-8 bytes
std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
/// index to the utf8_codes array for the next valid byte
std::size_t utf8_bytes_index = 0;
/// number of valid bytes in the utf8_codes array
std::size_t utf8_bytes_filled = 0;
};
template<typename IteratorType, typename Enable = void>
struct iterator_input_adapter_factory
{
using iterator_type = IteratorType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using adapter_type = iterator_input_adapter<iterator_type>;
static adapter_type create(IteratorType first, IteratorType last)
{
return adapter_type(std::move(first), std::move(last));
}
};
template<typename T>
struct is_iterator_of_multibyte
{
using value_type = typename std::iterator_traits<T>::value_type;
enum
{
value = sizeof(value_type) > 1
};
};
template<typename IteratorType>
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
{
using iterator_type = IteratorType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using base_adapter_type = iterator_input_adapter<iterator_type>;
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
static adapter_type create(IteratorType first, IteratorType last)
{
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
}
};
// General purpose iterator-based input
template<typename IteratorType>
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
{
using factory_type = iterator_input_adapter_factory<IteratorType>;
return factory_type::create(first, last);
}
// Convenience shorthand from container to iterator
template<typename ContainerType>
auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container)))
{
// Enable ADL
using std::begin;
using std::end;
return input_adapter(begin(container), end(container));
}
// Special cases with fast paths
inline file_input_adapter input_adapter(std::FILE* file)
{
return file_input_adapter(file);
}
inline input_stream_adapter input_adapter(std::istream& stream)
{
return input_stream_adapter(stream);
}
inline input_stream_adapter input_adapter(std::istream&& stream)
{
return input_stream_adapter(stream);
}
using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
// Null-delimited strings, and the like.
template < typename CharT,
typename std::enable_if <
std::is_pointer<CharT>::value&&
!std::is_array<CharT>::value&&
std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
sizeof(typename std::remove_pointer<CharT>::type) == 1,
int >::type = 0 >
contiguous_bytes_input_adapter input_adapter(CharT b)
{
auto length = std::strlen(reinterpret_cast<const char*>(b));
const auto* ptr = reinterpret_cast<const char*>(b);
return input_adapter(ptr, ptr + length);
}
template<typename T, std::size_t N>
auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N))
{
return input_adapter(array, array + N);
}
// This class only handles inputs of input_buffer_adapter type.
// It's required so that expressions like {ptr, len} can be implicitely casted
// to the correct adapter.
class span_input_adapter
{
public:
template < typename CharT,
typename std::enable_if <
std::is_pointer<CharT>::value&&
std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
sizeof(typename std::remove_pointer<CharT>::type) == 1,
int >::type = 0 >
span_input_adapter(CharT b, std::size_t l)
: ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
template<class IteratorType,
typename std::enable_if<
std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
int>::type = 0>
span_input_adapter(IteratorType first, IteratorType last)
: ia(input_adapter(first, last)) {}
contiguous_bytes_input_adapter&& get()
{
return std::move(ia);
}
private:
contiguous_bytes_input_adapter ia;
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,695 @@
#pragma once
#include <cstddef>
#include <string> // string
#include <utility> // move
#include <vector> // vector
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
/*!
@brief SAX interface
This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
Each function is called in different situations while the input is parsed. The
boolean return value informs the parser whether to continue processing the
input.
*/
template<typename BasicJsonType>
struct json_sax
{
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
/*!
@brief a null value was read
@return whether parsing should proceed
*/
virtual bool null() = 0;
/*!
@brief a boolean value was read
@param[in] val boolean value
@return whether parsing should proceed
*/
virtual bool boolean(bool val) = 0;
/*!
@brief an integer number was read
@param[in] val integer value
@return whether parsing should proceed
*/
virtual bool number_integer(number_integer_t val) = 0;
/*!
@brief an unsigned integer number was read
@param[in] val unsigned integer value
@return whether parsing should proceed
*/
virtual bool number_unsigned(number_unsigned_t val) = 0;
/*!
@brief an floating-point number was read
@param[in] val floating-point value
@param[in] s raw token value
@return whether parsing should proceed
*/
virtual bool number_float(number_float_t val, const string_t& s) = 0;
/*!
@brief a string was read
@param[in] val string value
@return whether parsing should proceed
@note It is safe to move the passed string.
*/
virtual bool string(string_t& val) = 0;
/*!
@brief a binary string was read
@param[in] val binary value
@return whether parsing should proceed
@note It is safe to move the passed binary.
*/
virtual bool binary(binary_t& val) = 0;
/*!
@brief the beginning of an object was read
@param[in] elements number of object elements or -1 if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
*/
virtual bool start_object(std::size_t elements) = 0;
/*!
@brief an object key was read
@param[in] val object key
@return whether parsing should proceed
@note It is safe to move the passed string.
*/
virtual bool key(string_t& val) = 0;
/*!
@brief the end of an object was read
@return whether parsing should proceed
*/
virtual bool end_object() = 0;
/*!
@brief the beginning of an array was read
@param[in] elements number of array elements or -1 if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
*/
virtual bool start_array(std::size_t elements) = 0;
/*!
@brief the end of an array was read
@return whether parsing should proceed
*/
virtual bool end_array() = 0;
/*!
@brief a parse error occurred
@param[in] position the position in the input where the error occurs
@param[in] last_token the last read token
@param[in] ex an exception object describing the error
@return whether parsing should proceed (must return false)
*/
virtual bool parse_error(std::size_t position,
const std::string& last_token,
const detail::exception& ex) = 0;
virtual ~json_sax() = default;
};
namespace detail
{
/*!
@brief SAX implementation to create a JSON value from SAX events
This class implements the @ref json_sax interface and processes the SAX events
to create a JSON value which makes it basically a DOM parser. The structure or
hierarchy of the JSON value is managed by the stack `ref_stack` which contains
a pointer to the respective array or object for each recursion depth.
After successful parsing, the value that is passed by reference to the
constructor contains the parsed value.
@tparam BasicJsonType the JSON type
*/
template<typename BasicJsonType>
class json_sax_dom_parser
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
/*!
@param[in, out] r reference to a JSON value that is manipulated while
parsing
@param[in] allow_exceptions_ whether parse errors yield exceptions
*/
explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
: root(r), allow_exceptions(allow_exceptions_)
{}
// make class move-only
json_sax_dom_parser(const json_sax_dom_parser&) = delete;
json_sax_dom_parser(json_sax_dom_parser&&) = default;
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() = default;
bool null()
{
handle_value(nullptr);
return true;
}
bool boolean(bool val)
{
handle_value(val);
return true;
}
bool number_integer(number_integer_t val)
{
handle_value(val);
return true;
}
bool number_unsigned(number_unsigned_t val)
{
handle_value(val);
return true;
}
bool number_float(number_float_t val, const string_t& /*unused*/)
{
handle_value(val);
return true;
}
bool string(string_t& val)
{
handle_value(val);
return true;
}
bool binary(binary_t& val)
{
handle_value(std::move(val));
return true;
}
bool start_object(std::size_t len)
{
ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408,
"excessive object size: " + std::to_string(len)));
}
return true;
}
bool key(string_t& val)
{
// add null at given key and store the reference for later
object_element = &(ref_stack.back()->m_value.object->operator[](val));
return true;
}
bool end_object()
{
ref_stack.pop_back();
return true;
}
bool start_array(std::size_t len)
{
ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408,
"excessive array size: " + std::to_string(len)));
}
return true;
}
bool end_array()
{
ref_stack.pop_back();
return true;
}
template<class Exception>
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
const Exception& ex)
{
errored = true;
static_cast<void>(ex);
if (allow_exceptions)
{
JSON_THROW(ex);
}
return false;
}
constexpr bool is_errored() const
{
return errored;
}
private:
/*!
@invariant If the ref stack is empty, then the passed value will be the new
root.
@invariant If the ref stack contains a value, then it is an array or an
object to which we can add elements
*/
template<typename Value>
JSON_HEDLEY_RETURNS_NON_NULL
BasicJsonType* handle_value(Value&& v)
{
if (ref_stack.empty())
{
root = BasicJsonType(std::forward<Value>(v));
return &root;
}
JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
if (ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
return &(ref_stack.back()->m_value.array->back());
}
JSON_ASSERT(ref_stack.back()->is_object());
JSON_ASSERT(object_element);
*object_element = BasicJsonType(std::forward<Value>(v));
return object_element;
}
/// the parsed JSON value
BasicJsonType& root;
/// stack to model hierarchy of values
std::vector<BasicJsonType*> ref_stack {};
/// helper to hold the reference for the next object element
BasicJsonType* object_element = nullptr;
/// whether a syntax error occurred
bool errored = false;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
};
template<typename BasicJsonType>
class json_sax_dom_callback_parser
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
using parser_callback_t = typename BasicJsonType::parser_callback_t;
using parse_event_t = typename BasicJsonType::parse_event_t;
json_sax_dom_callback_parser(BasicJsonType& r,
const parser_callback_t cb,
const bool allow_exceptions_ = true)
: root(r), callback(cb), allow_exceptions(allow_exceptions_)
{
keep_stack.push_back(true);
}
// make class move-only
json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default;
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() = default;
bool null()
{
handle_value(nullptr);
return true;
}
bool boolean(bool val)
{
handle_value(val);
return true;
}
bool number_integer(number_integer_t val)
{
handle_value(val);
return true;
}
bool number_unsigned(number_unsigned_t val)
{
handle_value(val);
return true;
}
bool number_float(number_float_t val, const string_t& /*unused*/)
{
handle_value(val);
return true;
}
bool string(string_t& val)
{
handle_value(val);
return true;
}
bool binary(binary_t& val)
{
handle_value(std::move(val));
return true;
}
bool start_object(std::size_t len)
{
// check callback for object start
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
keep_stack.push_back(keep);
auto val = handle_value(BasicJsonType::value_t::object, true);
ref_stack.push_back(val.second);
// check object limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len)));
}
return true;
}
bool key(string_t& val)
{
BasicJsonType k = BasicJsonType(val);
// check callback for key
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
key_keep_stack.push_back(keep);
// add discarded value at given key and store the reference for later
if (keep && ref_stack.back())
{
object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
}
return true;
}
bool end_object()
{
if (ref_stack.back() && !callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
{
// discard object
*ref_stack.back() = discarded;
}
JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
{
// remove discarded value
for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
{
if (it->is_discarded())
{
ref_stack.back()->erase(it);
break;
}
}
}
return true;
}
bool start_array(std::size_t len)
{
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
keep_stack.push_back(keep);
auto val = handle_value(BasicJsonType::value_t::array, true);
ref_stack.push_back(val.second);
// check array limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{
JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len)));
}
return true;
}
bool end_array()
{
bool keep = true;
if (ref_stack.back())
{
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
if (!keep)
{
// discard array
*ref_stack.back() = discarded;
}
}
JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
// remove discarded value
if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->pop_back();
}
return true;
}
template<class Exception>
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
const Exception& ex)
{
errored = true;
static_cast<void>(ex);
if (allow_exceptions)
{
JSON_THROW(ex);
}
return false;
}
constexpr bool is_errored() const
{
return errored;
}
private:
/*!
@param[in] v value to add to the JSON value we build during parsing
@param[in] skip_callback whether we should skip calling the callback
function; this is required after start_array() and
start_object() SAX events, because otherwise we would call the
callback function with an empty array or object, respectively.
@invariant If the ref stack is empty, then the passed value will be the new
root.
@invariant If the ref stack contains a value, then it is an array or an
object to which we can add elements
@return pair of boolean (whether value should be kept) and pointer (to the
passed value in the ref_stack hierarchy; nullptr if not kept)
*/
template<typename Value>
std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
{
JSON_ASSERT(!keep_stack.empty());
// do not handle this value if we know it would be added to a discarded
// container
if (!keep_stack.back())
{
return {false, nullptr};
}
// create value
auto value = BasicJsonType(std::forward<Value>(v));
// check callback
const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
// do not handle this value if we just learnt it shall be discarded
if (!keep)
{
return {false, nullptr};
}
if (ref_stack.empty())
{
root = std::move(value);
return {true, &root};
}
// skip this value if we already decided to skip the parent
// (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
if (!ref_stack.back())
{
return {false, nullptr};
}
// we now only expect arrays and objects
JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
// array
if (ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->push_back(std::move(value));
return {true, &(ref_stack.back()->m_value.array->back())};
}
// object
JSON_ASSERT(ref_stack.back()->is_object());
// check if we should store an element for the current key
JSON_ASSERT(!key_keep_stack.empty());
const bool store_element = key_keep_stack.back();
key_keep_stack.pop_back();
if (!store_element)
{
return {false, nullptr};
}
JSON_ASSERT(object_element);
*object_element = std::move(value);
return {true, object_element};
}
/// the parsed JSON value
BasicJsonType& root;
/// stack to model hierarchy of values
std::vector<BasicJsonType*> ref_stack {};
/// stack to manage which values to keep
std::vector<bool> keep_stack {};
/// stack to manage which object keys to keep
std::vector<bool> key_keep_stack {};
/// helper to hold the reference for the next object element
BasicJsonType* object_element = nullptr;
/// whether a syntax error occurred
bool errored = false;
/// callback function
const parser_callback_t callback = nullptr;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
/// a discarded value for the callback
BasicJsonType discarded = BasicJsonType::value_t::discarded;
};
template<typename BasicJsonType>
class json_sax_acceptor
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
bool null()
{
return true;
}
bool boolean(bool /*unused*/)
{
return true;
}
bool number_integer(number_integer_t /*unused*/)
{
return true;
}
bool number_unsigned(number_unsigned_t /*unused*/)
{
return true;
}
bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
{
return true;
}
bool string(string_t& /*unused*/)
{
return true;
}
bool binary(binary_t& /*unused*/)
{
return true;
}
bool start_object(std::size_t /*unused*/ = std::size_t(-1))
{
return true;
}
bool key(string_t& /*unused*/)
{
return true;
}
bool end_object()
{
return true;
}
bool start_array(std::size_t /*unused*/ = std::size_t(-1))
{
return true;
}
bool end_array()
{
return true;
}
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
{
return false;
}
};
} // namespace detail
} // namespace nlohmann

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,501 @@
#pragma once
#include <cmath> // isfinite
#include <cstdint> // uint8_t
#include <functional> // function
#include <string> // string
#include <utility> // move
#include <vector> // vector
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/input/input_adapters.hpp>
#include <nlohmann/detail/input/json_sax.hpp>
#include <nlohmann/detail/input/lexer.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/is_sax.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
////////////
// parser //
////////////
enum class parse_event_t : uint8_t
{
/// the parser read `{` and started to process a JSON object
object_start,
/// the parser read `}` and finished processing a JSON object
object_end,
/// the parser read `[` and started to process a JSON array
array_start,
/// the parser read `]` and finished processing a JSON array
array_end,
/// the parser read a key of a value in an object
key,
/// the parser finished reading a JSON value
value
};
template<typename BasicJsonType>
using parser_callback_t =
std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;
/*!
@brief syntax analysis
This class implements a recursive descent parser.
*/
template<typename BasicJsonType, typename InputAdapterType>
class parser
{
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using lexer_t = lexer<BasicJsonType, InputAdapterType>;
using token_type = typename lexer_t::token_type;
public:
/// a parser reading from an input adapter
explicit parser(InputAdapterType&& adapter,
const parser_callback_t<BasicJsonType> cb = nullptr,
const bool allow_exceptions_ = true,
const bool skip_comments = false)
: callback(cb)
, m_lexer(std::move(adapter), skip_comments)
, allow_exceptions(allow_exceptions_)
{
// read first token
get_token();
}
/*!
@brief public parser interface
@param[in] strict whether to expect the last token to be EOF
@param[in,out] result parsed JSON value
@throw parse_error.101 in case of an unexpected token
@throw parse_error.102 if to_unicode fails or surrogate error
@throw parse_error.103 if to_unicode fails
*/
void parse(const bool strict, BasicJsonType& result)
{
if (callback)
{
json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
sax_parse_internal(&sdp);
result.assert_invariant();
// in strict mode, input must be completely read
if (strict && (get_token() != token_type::end_of_input))
{
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value")));
}
// in case of an error, return discarded value
if (sdp.is_errored())
{
result = value_t::discarded;
return;
}
// set top-level value to null if it was discarded by the callback
// function
if (result.is_discarded())
{
result = nullptr;
}
}
else
{
json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
sax_parse_internal(&sdp);
result.assert_invariant();
// in strict mode, input must be completely read
if (strict && (get_token() != token_type::end_of_input))
{
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value")));
}
// in case of an error, return discarded value
if (sdp.is_errored())
{
result = value_t::discarded;
return;
}
}
}
/*!
@brief public accept interface
@param[in] strict whether to expect the last token to be EOF
@return whether the input is a proper JSON text
*/
bool accept(const bool strict = true)
{
json_sax_acceptor<BasicJsonType> sax_acceptor;
return sax_parse(&sax_acceptor, strict);
}
template<typename SAX>
JSON_HEDLEY_NON_NULL(2)
bool sax_parse(SAX* sax, const bool strict = true)
{
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
const bool result = sax_parse_internal(sax);
// strict mode: next byte must be EOF
if (result && strict && (get_token() != token_type::end_of_input))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value")));
}
return result;
}
private:
template<typename SAX>
JSON_HEDLEY_NON_NULL(2)
bool sax_parse_internal(SAX* sax)
{
// stack to remember the hierarchy of structured values we are parsing
// true = array; false = object
std::vector<bool> states;
// value to avoid a goto (see comment where set to true)
bool skip_to_state_evaluation = false;
while (true)
{
if (!skip_to_state_evaluation)
{
// invariant: get_token() was called before each iteration
switch (last_token)
{
case token_type::begin_object:
{
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))
{
return false;
}
// closing } -> we are done
if (get_token() == token_type::end_object)
{
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
{
return false;
}
break;
}
// parse key
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::value_string, "object key")));
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{
return false;
}
// parse separator (:)
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::name_separator, "object separator")));
}
// remember we are now inside an object
states.push_back(false);
// parse values
get_token();
continue;
}
case token_type::begin_array:
{
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))
{
return false;
}
// closing ] -> we are done
if (get_token() == token_type::end_array)
{
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
{
return false;
}
break;
}
// remember we are now inside an array
states.push_back(true);
// parse values (no need to call get_token)
continue;
}
case token_type::value_float:
{
const auto res = m_lexer.get_number_float();
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'"));
}
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
{
return false;
}
break;
}
case token_type::literal_false:
{
if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
{
return false;
}
break;
}
case token_type::literal_null:
{
if (JSON_HEDLEY_UNLIKELY(!sax->null()))
{
return false;
}
break;
}
case token_type::literal_true:
{
if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
{
return false;
}
break;
}
case token_type::value_integer:
{
if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
{
return false;
}
break;
}
case token_type::value_string:
{
if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
{
return false;
}
break;
}
case token_type::value_unsigned:
{
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
{
return false;
}
break;
}
case token_type::parse_error:
{
// using "uninitialized" to avoid "expected" message
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::uninitialized, "value")));
}
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")));
}
}
}
else
{
skip_to_state_evaluation = false;
}
// we reached this line after we successfully parsed a value
if (states.empty())
{
// empty stack: we reached the end of the hierarchy: done
return true;
}
if (states.back()) // array
{
// comma -> next value
if (get_token() == token_type::value_separator)
{
// parse a new value
get_token();
continue;
}
// closing ]
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
{
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
{
return false;
}
// We are done with this array. Before we can parse a
// new value, we need to evaluate the new state first.
// By setting skip_to_state_evaluation to false, we
// are effectively jumping to the beginning of this if.
JSON_ASSERT(!states.empty());
states.pop_back();
skip_to_state_evaluation = true;
continue;
}
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_array, "array")));
}
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(!sax->key(m_lexer.get_string())))
{
return false;
}
// parse separator (:)
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::name_separator, "object separator")));
}
// parse values
get_token();
continue;
}
// closing }
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
{
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
{
return false;
}
// We are done with this object. Before we can parse a
// new value, we need to evaluate the new state first.
// By setting skip_to_state_evaluation to false, we
// are effectively jumping to the beginning of this if.
JSON_ASSERT(!states.empty());
states.pop_back();
skip_to_state_evaluation = true;
continue;
}
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_object, "object")));
}
}
}
/// get next token from lexer
token_type get_token()
{
return last_token = m_lexer.scan();
}
std::string exception_message(const token_type expected, const std::string& context)
{
std::string error_msg = "syntax error ";
if (!context.empty())
{
error_msg += "while parsing " + context + " ";
}
error_msg += "- ";
if (last_token == token_type::parse_error)
{
error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
m_lexer.get_token_string() + "'";
}
else
{
error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
}
if (expected != token_type::uninitialized)
{
error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
}
return error_msg;
}
private:
/// callback function
const parser_callback_t<BasicJsonType> callback = nullptr;
/// the type of the last read token
token_type last_token = token_type::uninitialized;
/// the lexer
lexer_t m_lexer;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,27 @@
#pragma once
#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

View File

@ -0,0 +1,25 @@
#pragma once
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
namespace nlohmann
{
namespace detail
{
/*!
@brief an iterator value
@note This structure could easily be a union, but MSVC currently does not allow
unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
*/
template<typename BasicJsonType> struct internal_iterator
{
/// iterator for JSON objects
typename BasicJsonType::object_t::iterator object_iterator {};
/// iterator for JSON arrays
typename BasicJsonType::array_t::iterator array_iterator {};
/// generic iterator for all other types
primitive_iterator_t primitive_iterator {};
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,637 @@
#pragma once
#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
#include <type_traits> // conditional, is_const, remove_const
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/iterators/internal_iterator.hpp>
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
// forward declare, to be able to friend it later on
template<typename IteratorType> class iteration_proxy;
template<typename IteratorType> class iteration_proxy_value;
/*!
@brief a template for a bidirectional iterator for the @ref basic_json class
This class implements a both iterators (iterator and const_iterator) for the
@ref basic_json class.
@note An iterator is called *initialized* when a pointer to a JSON value has
been set (e.g., by a constructor or a copy assignment). If the iterator is
default-constructed, it is *uninitialized* and most methods are undefined.
**The library uses assertions to detect calls on uninitialized iterators.**
@requirement The class satisfies the following concept requirements:
-
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
The iterator that can be moved can be moved in both directions (i.e.
incremented and decremented).
@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
*/
template<typename BasicJsonType>
class iter_impl
{
/// 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 BasicJsonType;
friend iteration_proxy<iter_impl>;
friend iteration_proxy_value<iter_impl>;
using object_t = typename BasicJsonType::object_t;
using array_t = typename BasicJsonType::array_t;
// make sure BasicJsonType is basic_json or const basic_json
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
"iter_impl only accepts (const) basic_json");
public:
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
/// A user-defined iterator should provide publicly accessible typedefs named
/// iterator_category, value_type, difference_type, pointer, and reference.
/// Note that value_type is required to be non-const, even for constant iterators.
using iterator_category = std::bidirectional_iterator_tag;
/// the type of the values when the iterator is dereferenced
using value_type = typename BasicJsonType::value_type;
/// a type to represent differences between iterators
using difference_type = typename BasicJsonType::difference_type;
/// defines a pointer to the type iterated over (value_type)
using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
typename BasicJsonType::const_pointer,
typename BasicJsonType::pointer>::type;
/// defines a reference to the type iterated over (value_type)
using reference =
typename std::conditional<std::is_const<BasicJsonType>::value,
typename BasicJsonType::const_reference,
typename BasicJsonType::reference>::type;
/// default constructor
iter_impl() = default;
/*!
@brief constructor for a given JSON instance
@param[in] object pointer to a JSON object for this iterator
@pre object != nullptr
@post The iterator is initialized; i.e. `m_object != nullptr`.
*/
explicit iter_impl(pointer object) noexcept : m_object(object)
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
m_it.object_iterator = typename object_t::iterator();
break;
}
case value_t::array:
{
m_it.array_iterator = typename array_t::iterator();
break;
}
default:
{
m_it.primitive_iterator = primitive_iterator_t();
break;
}
}
}
/*!
@note The conventional copy constructor and copy assignment are implicitly
defined. Combined with the following converting constructor and
assignment, they support: (1) copy from iterator to iterator, (2)
copy from const iterator to const iterator, and (3) conversion from
iterator to const iterator. However conversion from const iterator
to iterator is not defined.
*/
/*!
@brief const copy constructor
@param[in] other const iterator to copy from
@note This copy constructor had to be defined explicitly to circumvent a bug
occurring on msvc v19.0 compiler (VS 2015) debug build. For more
information refer to: https://github.com/nlohmann/json/issues/1608
*/
iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
: m_object(other.m_object), m_it(other.m_it)
{}
/*!
@brief converting assignment
@param[in] other const iterator to copy from
@return const/non-const iterator
@note It is not checked whether @a other is initialized.
*/
iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
{
m_object = other.m_object;
m_it = other.m_it;
return *this;
}
/*!
@brief converting constructor
@param[in] other non-const iterator to copy from
@note It is not checked whether @a other is initialized.
*/
iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
: m_object(other.m_object), m_it(other.m_it)
{}
/*!
@brief converting assignment
@param[in] other non-const iterator to copy from
@return const/non-const iterator
@note It is not checked whether @a other is initialized.
*/
iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
{
m_object = other.m_object;
m_it = other.m_it;
return *this;
}
private:
/*!
@brief set the iterator to the first value
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
void set_begin() noexcept
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
m_it.object_iterator = m_object->m_value.object->begin();
break;
}
case value_t::array:
{
m_it.array_iterator = m_object->m_value.array->begin();
break;
}
case value_t::null:
{
// set to end so begin()==end() is true: null is empty
m_it.primitive_iterator.set_end();
break;
}
default:
{
m_it.primitive_iterator.set_begin();
break;
}
}
}
/*!
@brief set the iterator past the last value
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
void set_end() noexcept
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
m_it.object_iterator = m_object->m_value.object->end();
break;
}
case value_t::array:
{
m_it.array_iterator = m_object->m_value.array->end();
break;
}
default:
{
m_it.primitive_iterator.set_end();
break;
}
}
}
public:
/*!
@brief return a reference to the value pointed to by the iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference operator*() const
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
return m_it.object_iterator->second;
}
case value_t::array:
{
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"));
default:
{
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
{
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
}
}
}
/*!
@brief dereference the iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
pointer operator->() const
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
return &(m_it.object_iterator->second);
}
case value_t::array:
{
JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
return &*m_it.array_iterator;
}
default:
{
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
{
return m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
}
}
}
/*!
@brief post-increment (it++)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl const operator++(int)
{
auto result = *this;
++(*this);
return result;
}
/*!
@brief pre-increment (++it)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator++()
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
std::advance(m_it.object_iterator, 1);
break;
}
case value_t::array:
{
std::advance(m_it.array_iterator, 1);
break;
}
default:
{
++m_it.primitive_iterator;
break;
}
}
return *this;
}
/*!
@brief post-decrement (it--)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl const operator--(int)
{
auto result = *this;
--(*this);
return result;
}
/*!
@brief pre-decrement (--it)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator--()
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
{
std::advance(m_it.object_iterator, -1);
break;
}
case value_t::array:
{
std::advance(m_it.array_iterator, -1);
break;
}
default:
{
--m_it.primitive_iterator;
break;
}
}
return *this;
}
/*!
@brief comparison: equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator==(const iter_impl& other) const
{
// if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
}
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
return (m_it.object_iterator == other.m_it.object_iterator);
case value_t::array:
return (m_it.array_iterator == other.m_it.array_iterator);
default:
return (m_it.primitive_iterator == other.m_it.primitive_iterator);
}
}
/*!
@brief comparison: not equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator!=(const iter_impl& other) const
{
return !operator==(other);
}
/*!
@brief comparison: smaller
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<(const iter_impl& other) const
{
// if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
}
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"));
case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator);
default:
return (m_it.primitive_iterator < other.m_it.primitive_iterator);
}
}
/*!
@brief comparison: less than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<=(const iter_impl& other) const
{
return !other.operator < (*this);
}
/*!
@brief comparison: greater than
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>(const iter_impl& other) const
{
return !operator<=(other);
}
/*!
@brief comparison: greater than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>=(const iter_impl& other) const
{
return !operator<(other);
}
/*!
@brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator+=(difference_type i)
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
case value_t::array:
{
std::advance(m_it.array_iterator, i);
break;
}
default:
{
m_it.primitive_iterator += i;
break;
}
}
return *this;
}
/*!
@brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl& operator-=(difference_type i)
{
return operator+=(-i);
}
/*!
@brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl operator+(difference_type i) const
{
auto result = *this;
result += i;
return result;
}
/*!
@brief addition of distance and iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
friend iter_impl operator+(difference_type i, const iter_impl& it)
{
auto result = it;
result += i;
return result;
}
/*!
@brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
iter_impl operator-(difference_type i) const
{
auto result = *this;
result -= i;
return result;
}
/*!
@brief return difference
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
difference_type operator-(const iter_impl& other) const
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator;
default:
return m_it.primitive_iterator - other.m_it.primitive_iterator;
}
}
/*!
@brief access to successor
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference operator[](difference_type n) const
{
JSON_ASSERT(m_object != nullptr);
switch (m_object->m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
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"));
default:
{
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))
{
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
}
}
}
/*!
@brief return the key of an object iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const typename object_t::key_type& key() const
{
JSON_ASSERT(m_object != nullptr);
if (JSON_HEDLEY_LIKELY(m_object->is_object()))
{
return m_it.object_iterator->first;
}
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators"));
}
/*!
@brief return the value of an iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference value() const
{
return operator*();
}
private:
/// associated JSON instance
pointer m_object = nullptr;
/// the actual iterator of the associated instance
internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,178 @@
#pragma once
#include <cstddef> // size_t
#include <iterator> // input_iterator_tag
#include <string> // string, to_string
#include <tuple> // tuple_size, get, tuple_element
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
template<typename string_type>
void int_to_string( string_type& target, std::size_t value )
{
// For ADL
using std::to_string;
target = to_string(value);
}
template<typename IteratorType> class iteration_proxy_value
{
public:
using difference_type = std::ptrdiff_t;
using value_type = iteration_proxy_value;
using pointer = value_type * ;
using reference = value_type & ;
using iterator_category = std::input_iterator_tag;
using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
private:
/// the iterator
IteratorType anchor;
/// an index for arrays (used to create key names)
std::size_t array_index = 0;
/// last stringified array index
mutable std::size_t array_index_last = 0;
/// a string representation of the array index
mutable string_type array_index_str = "0";
/// an empty string (to return a reference for primitive values)
const string_type empty_str = "";
public:
explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {}
/// dereference operator (needed for range-based for)
iteration_proxy_value& operator*()
{
return *this;
}
/// increment operator (needed for range-based for)
iteration_proxy_value& operator++()
{
++anchor;
++array_index;
return *this;
}
/// equality operator (needed for InputIterator)
bool operator==(const iteration_proxy_value& o) const
{
return anchor == o.anchor;
}
/// inequality operator (needed for range-based for)
bool operator!=(const iteration_proxy_value& o) const
{
return anchor != o.anchor;
}
/// return key of the iterator
const string_type& key() const
{
JSON_ASSERT(anchor.m_object != nullptr);
switch (anchor.m_object->type())
{
// use integer array index as key
case value_t::array:
{
if (array_index != array_index_last)
{
int_to_string( array_index_str, array_index );
array_index_last = array_index;
}
return array_index_str;
}
// use key from the object
case value_t::object:
return anchor.key();
// use an empty key for all primitive types
default:
return empty_str;
}
}
/// return value of the iterator
typename IteratorType::reference value() const
{
return anchor.value();
}
};
/// proxy class for the items() function
template<typename IteratorType> class iteration_proxy
{
private:
/// the container to iterate
typename IteratorType::reference container;
public:
/// construct iteration proxy from a container
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
: container(cont) {}
/// return iterator begin (needed for range-based for)
iteration_proxy_value<IteratorType> begin() noexcept
{
return iteration_proxy_value<IteratorType>(container.begin());
}
/// return iterator end (needed for range-based for)
iteration_proxy_value<IteratorType> end() noexcept
{
return iteration_proxy_value<IteratorType>(container.end());
}
};
// Structured Bindings Support
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391
template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
{
return i.key();
}
// Structured Bindings Support
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391
template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
{
return i.value();
}
} // namespace detail
} // namespace nlohmann
// The Addition to the STD Namespace is required to add
// Structured Bindings Support to the iteration_proxy_value class
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391
namespace std
{
#if defined(__clang__)
// Fix: https://github.com/nlohmann/json/issues/1401
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif
template<typename IteratorType>
class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>
: public std::integral_constant<std::size_t, 2> {};
template<std::size_t N, typename IteratorType>
class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
{
public:
using type = decltype(
get<N>(std::declval <
::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
};
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
} // namespace std

View File

@ -0,0 +1,51 @@
#pragma once
#include <iterator> // random_access_iterator_tag
#include <nlohmann/detail/meta/void_t.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
namespace nlohmann
{
namespace detail
{
template<typename It, typename = void>
struct iterator_types {};
template<typename It>
struct iterator_types <
It,
void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
typename It::reference, typename It::iterator_category >>
{
using difference_type = typename It::difference_type;
using value_type = typename It::value_type;
using pointer = typename It::pointer;
using reference = typename It::reference;
using iterator_category = typename It::iterator_category;
};
// This is required as some compilers implement std::iterator_traits in a way that
// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
template<typename T, typename = void>
struct iterator_traits
{
};
template<typename T>
struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
: iterator_types<T>
{
};
template<typename T>
struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
{
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = T*;
using reference = T&;
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,119 @@
#pragma once
#include <cstddef> // ptrdiff_t
#include <iterator> // reverse_iterator
#include <utility> // declval
namespace nlohmann
{
namespace detail
{
//////////////////////
// reverse_iterator //
//////////////////////
/*!
@brief a template for a reverse iterator class
@tparam Base the base iterator type to reverse. Valid types are @ref
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
create @ref const_reverse_iterator).
@requirement The class satisfies the following concept requirements:
-
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
The iterator that can be moved can be moved in both directions (i.e.
incremented and decremented).
- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
It is possible to write to the pointed-to element (only if @a Base is
@ref iterator).
@since version 1.0.0
*/
template<typename Base>
class json_reverse_iterator : public std::reverse_iterator<Base>
{
public:
using difference_type = std::ptrdiff_t;
/// shortcut to the reverse iterator adapter
using base_iterator = std::reverse_iterator<Base>;
/// the reference type for the pointed-to element
using reference = typename Base::reference;
/// create reverse iterator from iterator
explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
: base_iterator(it) {}
/// create reverse iterator from base class
explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
/// post-increment (it++)
json_reverse_iterator const operator++(int)
{
return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
}
/// pre-increment (++it)
json_reverse_iterator& operator++()
{
return static_cast<json_reverse_iterator&>(base_iterator::operator++());
}
/// post-decrement (it--)
json_reverse_iterator const operator--(int)
{
return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
}
/// pre-decrement (--it)
json_reverse_iterator& operator--()
{
return static_cast<json_reverse_iterator&>(base_iterator::operator--());
}
/// add to iterator
json_reverse_iterator& operator+=(difference_type i)
{
return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
}
/// add to iterator
json_reverse_iterator operator+(difference_type i) const
{
return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
}
/// subtract from iterator
json_reverse_iterator operator-(difference_type i) const
{
return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
}
/// return difference
difference_type operator-(const json_reverse_iterator& other) const
{
return base_iterator(*this) - base_iterator(other);
}
/// access to successor
reference operator[](difference_type n) const
{
return *(this->operator+(n));
}
/// return the key of an object iterator
auto key() const -> decltype(std::declval<Base>().key())
{
auto it = --this->base();
return it.key();
}
/// return the value of an iterator
reference value() const
{
auto it = --this->base();
return it.operator * ();
}
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,120 @@
#pragma once
#include <cstddef> // ptrdiff_t
#include <limits> // numeric_limits
namespace nlohmann
{
namespace detail
{
/*
@brief an iterator for primitive JSON types
This class models an iterator for primitive JSON types (boolean, number,
string). It's only purpose is to allow the iterator/const_iterator classes
to "iterate" over primitive values. Internally, the iterator is modeled by
a `difference_type` variable. Value begin_value (`0`) models the begin,
end_value (`1`) models past the end.
*/
class primitive_iterator_t
{
private:
using difference_type = std::ptrdiff_t;
static constexpr difference_type begin_value = 0;
static constexpr difference_type end_value = begin_value + 1;
/// iterator as signed integer type
difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
public:
constexpr difference_type get_value() const noexcept
{
return m_it;
}
/// set iterator to a defined beginning
void set_begin() noexcept
{
m_it = begin_value;
}
/// set iterator to a defined past the end
void set_end() noexcept
{
m_it = end_value;
}
/// return whether the iterator can be dereferenced
constexpr bool is_begin() const noexcept
{
return m_it == begin_value;
}
/// return whether the iterator is at end
constexpr bool is_end() const noexcept
{
return m_it == end_value;
}
friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
{
return lhs.m_it == rhs.m_it;
}
friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
{
return lhs.m_it < rhs.m_it;
}
primitive_iterator_t operator+(difference_type n) noexcept
{
auto result = *this;
result += n;
return result;
}
friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
{
return lhs.m_it - rhs.m_it;
}
primitive_iterator_t& operator++() noexcept
{
++m_it;
return *this;
}
primitive_iterator_t const operator++(int) noexcept
{
auto result = *this;
++m_it;
return result;
}
primitive_iterator_t& operator--() noexcept
{
--m_it;
return *this;
}
primitive_iterator_t const operator--(int) noexcept
{
auto result = *this;
--m_it;
return result;
}
primitive_iterator_t& operator+=(difference_type n) noexcept
{
m_it += n;
return *this;
}
primitive_iterator_t& operator-=(difference_type n) noexcept
{
m_it -= n;
return *this;
}
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,975 @@
#pragma once
#include <algorithm> // all_of
#include <cctype> // isdigit
#include <limits> // max
#include <numeric> // accumulate
#include <string> // string
#include <utility> // move
#include <vector> // vector
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
template<typename BasicJsonType>
class json_pointer
{
// allow basic_json to access private members
NLOHMANN_BASIC_JSON_TPL_DECLARATION
friend class basic_json;
public:
/*!
@brief create JSON pointer
Create a JSON pointer according to the syntax described in
[Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
@param[in] s string representing the JSON pointer; if omitted, the empty
string is assumed which references the whole JSON value
@throw parse_error.107 if the given JSON pointer @a s is nonempty and does
not begin with a slash (`/`); see example below
@throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is
not followed by `0` (representing `~`) or `1` (representing `/`); see
example below
@liveexample{The example shows the construction several valid JSON pointers
as well as the exceptional behavior.,json_pointer}
@since version 2.0.0
*/
explicit json_pointer(const std::string& s = "")
: reference_tokens(split(s))
{}
/*!
@brief return a string representation of the JSON pointer
@invariant For each JSON pointer `ptr`, it holds:
@code {.cpp}
ptr == json_pointer(ptr.to_string());
@endcode
@return a string representation of the JSON pointer
@liveexample{The example shows the result of `to_string`.,json_pointer__to_string}
@since version 2.0.0
*/
std::string to_string() const
{
return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + escape(b);
});
}
/// @copydoc to_string()
operator std::string() const
{
return to_string();
}
/*!
@brief append another JSON pointer at the end of this JSON pointer
@param[in] ptr JSON pointer to append
@return JSON pointer with @a ptr appended
@liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}
@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
@since version 3.6.0
*/
json_pointer& operator/=(const json_pointer& ptr)
{
reference_tokens.insert(reference_tokens.end(),
ptr.reference_tokens.begin(),
ptr.reference_tokens.end());
return *this;
}
/*!
@brief append an unescaped reference token at the end of this JSON pointer
@param[in] token reference token to append
@return JSON pointer with @a token appended without escaping @a token
@liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}
@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
@since version 3.6.0
*/
json_pointer& operator/=(std::string token)
{
push_back(std::move(token));
return *this;
}
/*!
@brief append an array index at the end of this JSON pointer
@param[in] array_idx array index to append
@return JSON pointer with @a array_idx appended
@liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}
@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
@since version 3.6.0
*/
json_pointer& operator/=(std::size_t array_idx)
{
return *this /= std::to_string(array_idx);
}
/*!
@brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
@param[in] lhs JSON pointer
@param[in] rhs JSON pointer
@return a new JSON pointer with @a rhs appended to @a lhs
@liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}
@complexity Linear in the length of @a lhs and @a rhs.
@sa @ref operator/=(const json_pointer&) to append a JSON pointer
@since version 3.6.0
*/
friend json_pointer operator/(const json_pointer& lhs,
const json_pointer& rhs)
{
return json_pointer(lhs) /= rhs;
}
/*!
@brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
@param[in] ptr JSON pointer
@param[in] token reference token
@return a new JSON pointer with unescaped @a token appended to @a ptr
@liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}
@complexity Linear in the length of @a ptr.
@sa @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)
{
return json_pointer(ptr) /= std::move(token);
}
/*!
@brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
@param[in] ptr JSON pointer
@param[in] array_idx array index
@return a new JSON pointer with @a array_idx appended to @a ptr
@liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}
@complexity Linear in the length of @a ptr.
@sa @ref operator/=(std::size_t) to append an array index
@since version 3.6.0
*/
friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx)
{
return json_pointer(ptr) /= array_idx;
}
/*!
@brief returns the parent of this JSON pointer
@return parent of this JSON pointer; in case this JSON pointer is the root,
the root itself is returned
@complexity Linear in the length of the JSON pointer.
@liveexample{The example shows the result of `parent_pointer` for different
JSON Pointers.,json_pointer__parent_pointer}
@since version 3.6.0
*/
json_pointer parent_pointer() const
{
if (empty())
{
return *this;
}
json_pointer res = *this;
res.pop_back();
return res;
}
/*!
@brief remove last reference token
@pre not `empty()`
@liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back}
@complexity Constant.
@throw out_of_range.405 if JSON pointer has no parent
@since version 3.6.0
*/
void pop_back()
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
}
reference_tokens.pop_back();
}
/*!
@brief return last reference token
@pre not `empty()`
@return last reference token
@liveexample{The example shows the usage of `back`.,json_pointer__back}
@complexity Constant.
@throw out_of_range.405 if JSON pointer has no parent
@since version 3.6.0
*/
const std::string& back() const
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
}
return reference_tokens.back();
}
/*!
@brief append an unescaped token at the end of the reference pointer
@param[in] token token to add
@complexity Amortized constant.
@liveexample{The example shows the result of `push_back` for different
JSON Pointers.,json_pointer__push_back}
@since version 3.6.0
*/
void push_back(const std::string& token)
{
reference_tokens.push_back(token);
}
/// @copydoc push_back(const std::string&)
void push_back(std::string&& token)
{
reference_tokens.push_back(std::move(token));
}
/*!
@brief return whether pointer points to the root document
@return true iff the JSON pointer points to the root document
@complexity Constant.
@exceptionsafety No-throw guarantee: this function never throws exceptions.
@liveexample{The example shows the result of `empty` for different JSON
Pointers.,json_pointer__empty}
@since version 3.6.0
*/
bool empty() const noexcept
{
return reference_tokens.empty();
}
private:
/*!
@param[in] s reference token to be converted into an array index
@return integer representation of @a s
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index begins not with a digit
@throw out_of_range.404 if string @a s could not be converted to an integer
@throw out_of_range.410 if an array index exceeds size_type
*/
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 && s[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + s +
"' must not begin with '0'"));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number"));
}
std::size_t processed_chars = 0;
unsigned long long res = 0;
JSON_TRY
{
res = std::stoull(s, &processed_chars);
}
JSON_CATCH(std::out_of_range&)
{
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
}
// 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 + "'"));
}
// 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)()))
{
JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE
}
return static_cast<size_type>(res);
}
json_pointer top() const
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
}
json_pointer result = *this;
result.reference_tokens = {reference_tokens[0]};
return result;
}
/*!
@brief create and return a reference to the pointed to value
@complexity Linear in the number of reference tokens.
@throw parse_error.109 if array index is not a number
@throw type_error.313 if value cannot be unflattened
*/
BasicJsonType& get_and_create(BasicJsonType& j) const
{
auto result = &j;
// in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value
for (const auto& reference_token : reference_tokens)
{
switch (result->type())
{
case detail::value_t::null:
{
if (reference_token == "0")
{
// start a new array if reference token is 0
result = &result->operator[](0);
}
else
{
// start a new object otherwise
result = &result->operator[](reference_token);
}
break;
}
case detail::value_t::object:
{
// create an entry in the object
result = &result->operator[](reference_token);
break;
}
case detail::value_t::array:
{
// create an entry in the array
result = &result->operator[](array_index(reference_token));
break;
}
/*
The following code is only reached if there exists a reference
token _and_ the current value is primitive. In this case, we have
an error situation, because primitive values may only occur as
single value; that is, with an empty list of reference tokens.
*/
default:
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
}
}
return *result;
}
/*!
@brief return a reference to the pointed to value
@note This version does not throw if a value is not present, but tries to
create nested values instead. For instance, calling this function
with pointer `"/this/that"` on a null value is equivalent to calling
`operator[]("this").operator[]("that")` on that value, effectively
changing the null value to an object.
@param[in] ptr a JSON value
@return reference to the JSON value pointed to by the JSON pointer
@complexity Linear in the length of the JSON pointer.
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
BasicJsonType& get_unchecked(BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
// convert null values to arrays or objects before continuing
if (ptr->is_null())
{
// check if reference token is a number
const bool nums =
std::all_of(reference_token.begin(), reference_token.end(),
[](const unsigned char x)
{
return std::isdigit(x);
});
// change value to array for numbers or "-" or to object otherwise
*ptr = (nums || reference_token == "-")
? detail::value_t::array
: detail::value_t::object;
}
switch (ptr->type())
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
if (reference_token == "-")
{
// explicitly treat "-" as index beyond the end
ptr = &ptr->operator[](ptr->m_value.array->size());
}
else
{
// convert array index to number; unchecked access
ptr = &ptr->operator[](array_index(reference_token));
}
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
BasicJsonType& get_checked(BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// note: at performs range check
ptr = &ptr->at(array_index(reference_token));
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@brief return a const reference to the pointed to value
@param[in] ptr a JSON value
@return const reference to the JSON value pointed to by the JSON
pointer
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
{
case detail::value_t::object:
{
// use unchecked object access
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// use unchecked array access
ptr = &ptr->operator[](array_index(reference_token));
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@throw out_of_range.404 if the JSON pointer can not be resolved
*/
const BasicJsonType& get_checked(const BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
{
case detail::value_t::object:
{
// note: at performs range check
ptr = &ptr->at(reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
}
// note: at performs range check
ptr = &ptr->at(array_index(reference_token));
break;
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
}
}
return *ptr;
}
/*!
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
*/
bool contains(const BasicJsonType* ptr) const
{
for (const auto& reference_token : reference_tokens)
{
switch (ptr->type())
{
case detail::value_t::object:
{
if (!ptr->contains(reference_token))
{
// we did not find the key in the object
return false;
}
ptr = &ptr->operator[](reference_token);
break;
}
case detail::value_t::array:
{
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" always fails the range check
return false;
}
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
{
// invalid char
return false;
}
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
{
if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
{
// first char should be between '1' and '9'
return false;
}
for (std::size_t i = 1; i < reference_token.size(); i++)
{
if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
{
// other char should be between '0' and '9'
return false;
}
}
}
const auto idx = array_index(reference_token);
if (idx >= ptr->size())
{
// index out of range
return false;
}
ptr = &ptr->operator[](idx);
break;
}
default:
{
// we do not expect primitive values if there is still a
// reference token to process
return false;
}
}
}
// no reference token left means we found a primitive value
return true;
}
/*!
@brief split the string input to reference tokens
@note This function is only called by the json_pointer constructor.
All exceptions below are documented there.
@throw parse_error.107 if the pointer is not empty or begins with '/'
@throw parse_error.108 if character '~' is not followed by '0' or '1'
*/
static std::vector<std::string> split(const std::string& reference_string)
{
std::vector<std::string> result;
// special case: empty reference string -> no reference tokens
if (reference_string.empty())
{
return result;
}
// check if nonempty reference string begins with slash
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
{
JSON_THROW(detail::parse_error::create(107, 1,
"JSON pointer must be empty or begin with '/' - was: '" +
reference_string + "'"));
}
// extract the reference tokens:
// - slash: position of the last read slash (or end of string)
// - start: position after the previous slash
for (
// search for the first slash after the first character
std::size_t slash = reference_string.find_first_of('/', 1),
// set the beginning of the first reference token
start = 1;
// we can stop if start == 0 (if slash == std::string::npos)
start != 0;
// set the beginning of the next reference token
// (will eventually be 0 if slash == std::string::npos)
start = (slash == std::string::npos) ? 0 : slash + 1,
// find next slash
slash = reference_string.find_first_of('/', start))
{
// use the text between the beginning of the reference token
// (start) and the last slash (slash).
auto reference_token = reference_string.substr(start, slash - start);
// check reference tokens are properly escaped
for (std::size_t pos = reference_token.find_first_of('~');
pos != std::string::npos;
pos = reference_token.find_first_of('~', pos + 1))
{
JSON_ASSERT(reference_token[pos] == '~');
// ~ must be followed by 0 or 1
if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
(reference_token[pos + 1] != '0' &&
reference_token[pos + 1] != '1')))
{
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
}
}
// finally, store the reference token
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)
{
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
{}
}
/// 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", "~");
}
/*!
@param[in] reference_string the reference string to the current value
@param[in] value the value to consider
@param[in,out] result the result object to insert values to
@note Empty objects or arrays are flattened to `null`.
*/
static void flatten(const std::string& reference_string,
const BasicJsonType& value,
BasicJsonType& result)
{
switch (value.type())
{
case detail::value_t::array:
{
if (value.m_value.array->empty())
{
// flatten empty array as null
result[reference_string] = nullptr;
}
else
{
// iterate array and use index as reference string
for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
{
flatten(reference_string + "/" + std::to_string(i),
value.m_value.array->operator[](i), result);
}
}
break;
}
case detail::value_t::object:
{
if (value.m_value.object->empty())
{
// flatten empty object as null
result[reference_string] = nullptr;
}
else
{
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
flatten(reference_string + "/" + escape(element.first), element.second, result);
}
}
break;
}
default:
{
// add primitive value with its reference string
result[reference_string] = value;
break;
}
}
}
/*!
@param[in] value flattened JSON
@return unflattened JSON
@throw parse_error.109 if array index is not a number
@throw type_error.314 if value is not an object
@throw type_error.315 if object values are not primitive
@throw type_error.313 if value cannot be unflattened
*/
static BasicJsonType
unflatten(const BasicJsonType& value)
{
if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
{
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
}
BasicJsonType result;
// iterate the JSON object values
for (const auto& element : *value.m_value.object)
{
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
{
JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
}
// assign value to reference pointed to by JSON pointer; Note that if
// the JSON pointer is "" (i.e., points to the whole value), function
// get_and_create returns a reference to result itself. An assignment
// will then create a primitive value.
json_pointer(element.first).get_and_create(result) = element.second;
}
return result;
}
/*!
@brief compares two JSON pointers for equality
@param[in] lhs JSON pointer to compare
@param[in] rhs JSON pointer to compare
@return whether @a lhs is equal to @a rhs
@complexity Linear in the length of the JSON pointer
@exceptionsafety No-throw guarantee: this function never throws exceptions.
*/
friend bool operator==(json_pointer const& lhs,
json_pointer const& rhs) noexcept
{
return lhs.reference_tokens == rhs.reference_tokens;
}
/*!
@brief compares two JSON pointers for inequality
@param[in] lhs JSON pointer to compare
@param[in] rhs JSON pointer to compare
@return whether @a lhs is not equal @a rhs
@complexity Linear in the length of the JSON pointer
@exceptionsafety No-throw guarantee: this function never throws exceptions.
*/
friend bool operator!=(json_pointer const& lhs,
json_pointer const& rhs) noexcept
{
return !(lhs == rhs);
}
/// the reference tokens
std::vector<std::string> reference_tokens;
};
} // namespace nlohmann

View File

@ -0,0 +1,76 @@
#pragma once
#include <initializer_list>
#include <utility>
#include <nlohmann/detail/meta/type_traits.hpp>
namespace nlohmann
{
namespace detail
{
template<typename BasicJsonType>
class json_ref
{
public:
using value_type = BasicJsonType;
json_ref(value_type&& value)
: owned_value(std::move(value))
, value_ref(&owned_value)
, is_rvalue(true)
{}
json_ref(const value_type& value)
: value_ref(const_cast<value_type*>(&value))
, is_rvalue(false)
{}
json_ref(std::initializer_list<json_ref> init)
: owned_value(init)
, value_ref(&owned_value)
, is_rvalue(true)
{}
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)
{}
// class should be movable only
json_ref(json_ref&&) = default;
json_ref(const json_ref&) = delete;
json_ref& operator=(const json_ref&) = delete;
json_ref& operator=(json_ref&&) = delete;
~json_ref() = default;
value_type moved_or_copied() const
{
if (is_rvalue)
{
return std::move(*value_ref);
}
return *value_ref;
}
value_type const& operator*() const
{
return *static_cast<value_type const*>(value_ref);
}
value_type const* operator->() const
{
return static_cast<value_type const*>(value_ref);
}
private:
mutable value_type owned_value = nullptr;
value_type* value_ref = nullptr;
const bool is_rvalue = true;
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,296 @@
#pragma once
#include <utility> // pair
#include <nlohmann/thirdparty/hedley/hedley.hpp>
// This file contains all internal macro definitions
// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
// exclude unsupported compilers
#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
#if defined(__clang__)
#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
#endif
#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
#endif
#endif
#endif
// C++ language standard detection
#if (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
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
// disable documentation warnings on clang
#if defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdocumentation"
#endif
// allow to disable exceptions
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
#define JSON_THROW(exception) throw exception
#define JSON_TRY try
#define JSON_CATCH(exception) catch(exception)
#define JSON_INTERNAL_CATCH(exception) catch(exception)
#else
#include <cstdlib>
#define JSON_THROW(exception) std::abort()
#define JSON_TRY if(true)
#define JSON_CATCH(exception) if(false)
#define JSON_INTERNAL_CATCH(exception) if(false)
#endif
// override exception macros
#if defined(JSON_THROW_USER)
#undef JSON_THROW
#define JSON_THROW JSON_THROW_USER
#endif
#if defined(JSON_TRY_USER)
#undef JSON_TRY
#define JSON_TRY JSON_TRY_USER
#endif
#if defined(JSON_CATCH_USER)
#undef JSON_CATCH
#define JSON_CATCH JSON_CATCH_USER
#undef JSON_INTERNAL_CATCH
#define JSON_INTERNAL_CATCH JSON_CATCH_USER
#endif
#if defined(JSON_INTERNAL_CATCH_USER)
#undef JSON_INTERNAL_CATCH
#define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
#endif
// allow to override assert
#if !defined(JSON_ASSERT)
#include <cassert> // assert
#define JSON_ASSERT(x) assert(x)
#endif
/*!
@brief macro to briefly define a mapping between an enum and JSON
@def NLOHMANN_JSON_SERIALIZE_ENUM
@since version 3.4.0
*/
#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \
template<typename BasicJsonType> \
inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \
{ \
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
auto it = std::find_if(std::begin(m), std::end(m), \
[e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
{ \
return ej_pair.first == e; \
}); \
j = ((it != std::end(m)) ? it : std::begin(m))->second; \
} \
template<typename BasicJsonType> \
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
{ \
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
auto it = std::find_if(std::begin(m), std::end(m), \
[&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
{ \
return ej_pair.second == j; \
}); \
e = ((it != std::end(m)) ? it : std::begin(m))->first; \
}
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
// may be removed in the future once the class is split.
#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
template<template<typename, typename, typename...> class ObjectType, \
template<typename, typename...> class ArrayType, \
class StringType, class BooleanType, class NumberIntegerType, \
class NumberUnsignedType, class NumberFloatType, \
template<typename> class AllocatorType, \
template<typename, typename = void> class JSONSerializer, \
class BinaryType>
#define NLOHMANN_BASIC_JSON_TPL \
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
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

View File

@ -0,0 +1,23 @@
#pragma once
// 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_HAS_CPP_14
#undef JSON_HAS_CPP_17
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
#undef NLOHMANN_BASIC_JSON_TPL
#undef JSON_EXPLICIT
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>

View File

@ -0,0 +1,62 @@
#pragma once
#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
namespace nlohmann
{
namespace detail
{
// 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;
// implementation of C++14 index_sequence and affiliates
// source: https://stackoverflow.com/a/32223343
template<std::size_t... Ints>
struct index_sequence
{
using type = index_sequence;
using value_type = std::size_t;
static constexpr std::size_t size() noexcept
{
return sizeof...(Ints);
}
};
template<class Sequence1, class Sequence2>
struct merge_and_renumber;
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<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 > {};
template<> struct make_index_sequence<0> : index_sequence<> {};
template<> struct make_index_sequence<1> : index_sequence<0> {};
template<typename... Ts>
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
// dispatch utility (taken from ranges-v3)
template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
template<> struct priority_tag<0> {};
// taken from ranges-v3
template<typename T>
struct static_const
{
static constexpr T value{};
};
template<typename T>
constexpr T static_const<T>::value;
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,58 @@
#pragma once
#include <type_traits>
#include <nlohmann/detail/meta/void_t.hpp>
// https://en.cppreference.com/w/cpp/experimental/is_detected
namespace nlohmann
{
namespace detail
{
struct nonesuch
{
nonesuch() = delete;
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
nonesuch(nonesuch const&&) = delete;
void operator=(nonesuch const&) = delete;
void operator=(nonesuch&&) = delete;
};
template<class Default,
class AlwaysVoid,
template<class...> class Op,
class... Args>
struct detector
{
using value_t = std::false_type;
using type = Default;
};
template<class Default, template<class...> class Op, class... Args>
struct detector<Default, void_t<Op<Args...>>, Op, Args...>
{
using value_t = std::true_type;
using type = Op<Args...>;
};
template<template<class...> class Op, class... Args>
using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
template<template<class...> class Op, class... Args>
using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
template<class Default, template<class...> class Op, class... Args>
using detected_or = detector<Default, void, Op, Args...>;
template<class Default, template<class...> class Op, class... Args>
using detected_or_t = typename detected_or<Default, Op, Args...>::type;
template<class Expected, template<class...> class Op, class... Args>
using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
template<class To, template<class...> class Op, class... Args>
using is_detected_convertible =
std::is_convertible<detected_t<Op, Args...>, To>;
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,149 @@
#pragma once
#include <cstdint> // size_t
#include <utility> // declval
#include <string> // string
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
namespace nlohmann
{
namespace detail
{
template<typename T>
using null_function_t = decltype(std::declval<T&>().null());
template<typename T>
using boolean_function_t =
decltype(std::declval<T&>().boolean(std::declval<bool>()));
template<typename T, typename Integer>
using number_integer_function_t =
decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
template<typename T, typename Unsigned>
using number_unsigned_function_t =
decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));
template<typename T, typename Float, typename String>
using number_float_function_t = decltype(std::declval<T&>().number_float(
std::declval<Float>(), std::declval<const String&>()));
template<typename T, typename String>
using string_function_t =
decltype(std::declval<T&>().string(std::declval<String&>()));
template<typename T, typename Binary>
using binary_function_t =
decltype(std::declval<T&>().binary(std::declval<Binary&>()));
template<typename T>
using start_object_function_t =
decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));
template<typename T, typename String>
using key_function_t =
decltype(std::declval<T&>().key(std::declval<String&>()));
template<typename T>
using end_object_function_t = decltype(std::declval<T&>().end_object());
template<typename T>
using start_array_function_t =
decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
template<typename T>
using end_array_function_t = decltype(std::declval<T&>().end_array());
template<typename T, typename Exception>
using parse_error_function_t = decltype(std::declval<T&>().parse_error(
std::declval<std::size_t>(), std::declval<const std::string&>(),
std::declval<const Exception&>()));
template<typename SAX, typename BasicJsonType>
struct is_sax
{
private:
static_assert(is_basic_json<BasicJsonType>::value,
"BasicJsonType must be of type basic_json<...>");
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
using exception_t = typename BasicJsonType::exception;
public:
static constexpr bool value =
is_detected_exact<bool, null_function_t, SAX>::value &&
is_detected_exact<bool, boolean_function_t, SAX>::value &&
is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&
is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&
is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&
is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&
is_detected_exact<bool, start_object_function_t, SAX>::value &&
is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
is_detected_exact<bool, end_object_function_t, SAX>::value &&
is_detected_exact<bool, start_array_function_t, SAX>::value &&
is_detected_exact<bool, end_array_function_t, SAX>::value &&
is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
};
template<typename SAX, typename BasicJsonType>
struct is_sax_static_asserts
{
private:
static_assert(is_basic_json<BasicJsonType>::value,
"BasicJsonType must be of type basic_json<...>");
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using number_float_t = typename BasicJsonType::number_float_t;
using string_t = typename BasicJsonType::string_t;
using binary_t = typename BasicJsonType::binary_t;
using exception_t = typename BasicJsonType::exception;
public:
static_assert(is_detected_exact<bool, null_function_t, SAX>::value,
"Missing/invalid function: bool null()");
static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
"Missing/invalid function: bool boolean(bool)");
static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
"Missing/invalid function: bool boolean(bool)");
static_assert(
is_detected_exact<bool, number_integer_function_t, SAX,
number_integer_t>::value,
"Missing/invalid function: bool number_integer(number_integer_t)");
static_assert(
is_detected_exact<bool, number_unsigned_function_t, SAX,
number_unsigned_t>::value,
"Missing/invalid function: bool number_unsigned(number_unsigned_t)");
static_assert(is_detected_exact<bool, number_float_function_t, SAX,
number_float_t, string_t>::value,
"Missing/invalid function: bool number_float(number_float_t, const string_t&)");
static_assert(
is_detected_exact<bool, string_function_t, SAX, string_t>::value,
"Missing/invalid function: bool string(string_t&)");
static_assert(
is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,
"Missing/invalid function: bool binary(binary_t&)");
static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,
"Missing/invalid function: bool start_object(std::size_t)");
static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,
"Missing/invalid function: bool key(string_t&)");
static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,
"Missing/invalid function: bool end_object()");
static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,
"Missing/invalid function: bool start_array(std::size_t)");
static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,
"Missing/invalid function: bool end_array()");
static_assert(
is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,
"Missing/invalid function: bool parse_error(std::size_t, const "
"std::string&, const exception&)");
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,396 @@
#pragma once
#include <limits> // numeric_limits
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
#include <utility> // declval
#include <nlohmann/detail/iterators/iterator_traits.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/json_fwd.hpp>
namespace nlohmann
{
/*!
@brief detail namespace with internal helper functions
This namespace collects functions that should not be exposed,
implementations of some @ref basic_json methods, and meta-programming helpers.
@since version 2.1.0
*/
namespace detail
{
/////////////
// helpers //
/////////////
// Note to maintainers:
//
// Every trait in this file expects a non CV-qualified type.
// The only exceptions are in the 'aliases for detected' section
// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
//
// In this case, T has to be properly CV-qualified to constraint the function arguments
// (e.g. to_json(BasicJsonType&, const T&))
template<typename> struct is_basic_json : std::false_type {};
NLOHMANN_BASIC_JSON_TPL_DECLARATION
struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
//////////////////////
// json_ref helpers //
//////////////////////
template<typename>
class json_ref;
template<typename>
struct is_json_ref : std::false_type {};
template<typename T>
struct is_json_ref<json_ref<T>> : std::true_type {};
//////////////////////////
// aliases for detected //
//////////////////////////
template<typename T>
using mapped_type_t = typename T::mapped_type;
template<typename T>
using key_type_t = typename T::key_type;
template<typename T>
using value_type_t = typename T::value_type;
template<typename T>
using difference_type_t = typename T::difference_type;
template<typename T>
using pointer_t = typename T::pointer;
template<typename T>
using reference_t = typename T::reference;
template<typename T>
using iterator_category_t = typename T::iterator_category;
template<typename T>
using iterator_t = typename T::iterator;
template<typename T, typename... Args>
using to_json_function = decltype(T::to_json(std::declval<Args>()...));
template<typename T, typename... Args>
using from_json_function = decltype(T::from_json(std::declval<Args>()...));
template<typename T, typename U>
using get_template_function = decltype(std::declval<T>().template get<U>());
// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
template<typename BasicJsonType, typename T, typename = void>
struct has_from_json : std::false_type {};
// trait checking if j.get<T> is valid
// use this trait instead of std::is_constructible or std::is_convertible,
// both rely on, or make use of implicit conversions, and thus fail when T
// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
template <typename BasicJsonType, typename T>
struct is_getable
{
static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
};
template<typename BasicJsonType, typename T>
struct has_from_json < BasicJsonType, T,
enable_if_t < !is_basic_json<T>::value >>
{
using serializer = typename BasicJsonType::template json_serializer<T, void>;
static constexpr bool value =
is_detected_exact<void, from_json_function, serializer,
const BasicJsonType&, T&>::value;
};
// This trait checks if JSONSerializer<T>::from_json(json const&) exists
// this overload is used for non-default-constructible user-defined-types
template<typename BasicJsonType, typename T, typename = void>
struct has_non_default_from_json : std::false_type {};
template<typename BasicJsonType, typename T>
struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
{
using serializer = typename BasicJsonType::template json_serializer<T, void>;
static constexpr bool value =
is_detected_exact<T, from_json_function, serializer,
const BasicJsonType&>::value;
};
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
template<typename BasicJsonType, typename T, typename = void>
struct has_to_json : std::false_type {};
template<typename BasicJsonType, typename T>
struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
{
using serializer = typename BasicJsonType::template json_serializer<T, void>;
static constexpr bool value =
is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
T>::value;
};
///////////////////
// is_ functions //
///////////////////
template<typename T, typename = void>
struct is_iterator_traits : std::false_type {};
template<typename T>
struct is_iterator_traits<iterator_traits<T>>
{
private:
using traits = iterator_traits<T>;
public:
static constexpr auto value =
is_detected<value_type_t, traits>::value &&
is_detected<difference_type_t, traits>::value &&
is_detected<pointer_t, traits>::value &&
is_detected<iterator_category_t, traits>::value &&
is_detected<reference_t, traits>::value;
};
// source: https://stackoverflow.com/a/37193089/4116453
template<typename T, typename = void>
struct is_complete_type : std::false_type {};
template<typename T>
struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
template<typename BasicJsonType, typename CompatibleObjectType,
typename = void>
struct is_compatible_object_type_impl : std::false_type {};
template<typename BasicJsonType, typename CompatibleObjectType>
struct is_compatible_object_type_impl <
BasicJsonType, CompatibleObjectType,
enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&
is_detected<key_type_t, CompatibleObjectType>::value >>
{
using object_t = typename BasicJsonType::object_t;
// macOS's is_constructible does not play well with nonesuch...
static constexpr bool value =
std::is_constructible<typename object_t::key_type,
typename CompatibleObjectType::key_type>::value &&
std::is_constructible<typename object_t::mapped_type,
typename CompatibleObjectType::mapped_type>::value;
};
template<typename BasicJsonType, typename CompatibleObjectType>
struct is_compatible_object_type
: is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
template<typename BasicJsonType, typename ConstructibleObjectType,
typename = void>
struct is_constructible_object_type_impl : std::false_type {};
template<typename BasicJsonType, typename ConstructibleObjectType>
struct is_constructible_object_type_impl <
BasicJsonType, ConstructibleObjectType,
enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&
is_detected<key_type_t, ConstructibleObjectType>::value >>
{
using object_t = typename BasicJsonType::object_t;
static constexpr bool value =
(std::is_default_constructible<ConstructibleObjectType>::value &&
(std::is_move_assignable<ConstructibleObjectType>::value ||
std::is_copy_assignable<ConstructibleObjectType>::value) &&
(std::is_constructible<typename ConstructibleObjectType::key_type,
typename object_t::key_type>::value &&
std::is_same <
typename object_t::mapped_type,
typename ConstructibleObjectType::mapped_type >::value)) ||
(has_from_json<BasicJsonType,
typename ConstructibleObjectType::mapped_type>::value ||
has_non_default_from_json <
BasicJsonType,
typename ConstructibleObjectType::mapped_type >::value);
};
template<typename BasicJsonType, typename ConstructibleObjectType>
struct is_constructible_object_type
: is_constructible_object_type_impl<BasicJsonType,
ConstructibleObjectType> {};
template<typename BasicJsonType, typename CompatibleStringType,
typename = void>
struct is_compatible_string_type_impl : std::false_type {};
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;
};
template<typename BasicJsonType, typename ConstructibleStringType>
struct is_compatible_string_type
: is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
template<typename BasicJsonType, typename ConstructibleStringType,
typename = void>
struct is_constructible_string_type_impl : std::false_type {};
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,
typename BasicJsonType::string_t>::value;
};
template<typename BasicJsonType, typename ConstructibleStringType>
struct is_constructible_string_type
: is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {};
template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
struct is_compatible_array_type_impl : std::false_type {};
template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type_impl <
BasicJsonType, CompatibleArrayType,
enable_if_t < is_detected<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.
!is_iterator_traits <
iterator_traits<CompatibleArrayType >>::value >>
{
static constexpr bool value =
std::is_constructible<BasicJsonType,
typename CompatibleArrayType::value_type>::value;
};
template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type
: is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
struct is_constructible_array_type_impl : std::false_type {};
template<typename BasicJsonType, typename ConstructibleArrayType>
struct is_constructible_array_type_impl <
BasicJsonType, ConstructibleArrayType,
enable_if_t<std::is_same<ConstructibleArrayType,
typename BasicJsonType::value_type>::value >>
: std::true_type {};
template<typename BasicJsonType, typename ConstructibleArrayType>
struct is_constructible_array_type_impl <
BasicJsonType, ConstructibleArrayType,
enable_if_t < !std::is_same<ConstructibleArrayType,
typename BasicJsonType::value_type>::value&&
std::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,
// furthermore, std::back_insert_iterator (and other iterators) have a
// base class `iterator`... Therefore it is detected as a
// ConstructibleArrayType. The real fix would be to have an Iterable
// concept.
!is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value &&
(std::is_same<typename ConstructibleArrayType::value_type,
typename BasicJsonType::array_t::value_type>::value ||
has_from_json<BasicJsonType,
typename ConstructibleArrayType::value_type>::value ||
has_non_default_from_json <
BasicJsonType, typename ConstructibleArrayType::value_type >::value);
};
template<typename BasicJsonType, typename ConstructibleArrayType>
struct is_constructible_array_type
: is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
template<typename RealIntegerType, typename CompatibleNumberIntegerType,
typename = void>
struct is_compatible_integer_type_impl : std::false_type {};
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
struct is_compatible_integer_type_impl <
RealIntegerType, CompatibleNumberIntegerType,
enable_if_t < std::is_integral<RealIntegerType>::value&&
std::is_integral<CompatibleNumberIntegerType>::value&&
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
{
// is there an assert somewhere on overflows?
using RealLimits = std::numeric_limits<RealIntegerType>;
using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
static constexpr auto value =
std::is_constructible<RealIntegerType,
CompatibleNumberIntegerType>::value &&
CompatibleLimits::is_integer &&
RealLimits::is_signed == CompatibleLimits::is_signed;
};
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
struct is_compatible_integer_type
: is_compatible_integer_type_impl<RealIntegerType,
CompatibleNumberIntegerType> {};
template<typename BasicJsonType, typename CompatibleType, typename = void>
struct is_compatible_type_impl: std::false_type {};
template<typename BasicJsonType, typename CompatibleType>
struct is_compatible_type_impl <
BasicJsonType, CompatibleType,
enable_if_t<is_complete_type<CompatibleType>::value >>
{
static constexpr bool value =
has_to_json<BasicJsonType, CompatibleType>::value;
};
template<typename BasicJsonType, typename CompatibleType>
struct is_compatible_type
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
// 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>
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>...> {};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,13 @@
#pragma once
namespace nlohmann
{
namespace detail
{
template<typename ...Ts> struct make_void
{
using type = void;
};
template<typename ...Ts> using void_t = typename make_void<Ts...>::type;
} // namespace detail
} // namespace nlohmann

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
#pragma once
#include <algorithm> // copy
#include <cstddef> // size_t
#include <ios> // streamsize
#include <iterator> // back_inserter
#include <memory> // shared_ptr, make_shared
#include <ostream> // basic_ostream
#include <string> // basic_string
#include <vector> // vector
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
/// abstract output adapter interface
template<typename CharType> struct output_adapter_protocol
{
virtual void write_character(CharType c) = 0;
virtual void write_characters(const CharType* s, std::size_t length) = 0;
virtual ~output_adapter_protocol() = default;
};
/// a type to simplify interfaces
template<typename CharType>
using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
/// output adapter for byte vectors
template<typename CharType>
class output_vector_adapter : public output_adapter_protocol<CharType>
{
public:
explicit output_vector_adapter(std::vector<CharType>& vec) noexcept
: v(vec)
{}
void write_character(CharType c) override
{
v.push_back(c);
}
JSON_HEDLEY_NON_NULL(2)
void write_characters(const CharType* s, std::size_t length) override
{
std::copy(s, s + length, std::back_inserter(v));
}
private:
std::vector<CharType>& v;
};
/// output adapter for output streams
template<typename CharType>
class output_stream_adapter : public output_adapter_protocol<CharType>
{
public:
explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept
: stream(s)
{}
void write_character(CharType c) override
{
stream.put(c);
}
JSON_HEDLEY_NON_NULL(2)
void write_characters(const CharType* s, std::size_t length) override
{
stream.write(s, static_cast<std::streamsize>(length));
}
private:
std::basic_ostream<CharType>& stream;
};
/// output adapter for basic_string
template<typename CharType, typename StringType = std::basic_string<CharType>>
class output_string_adapter : public output_adapter_protocol<CharType>
{
public:
explicit output_string_adapter(StringType& s) noexcept
: str(s)
{}
void write_character(CharType c) override
{
str.push_back(c);
}
JSON_HEDLEY_NON_NULL(2)
void write_characters(const CharType* s, std::size_t length) override
{
str.append(s, length);
}
private:
StringType& str;
};
template<typename CharType, typename StringType = std::basic_string<CharType>>
class output_adapter
{
public:
output_adapter(std::vector<CharType>& vec)
: oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}
output_adapter(std::basic_ostream<CharType>& s)
: oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
output_adapter(StringType& s)
: oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
operator output_adapter_t<CharType>()
{
return oa;
}
private:
output_adapter_t<CharType> oa = nullptr;
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,947 @@
#pragma once
#include <algorithm> // reverse, remove, fill, find, none_of
#include <array> // array
#include <clocale> // localeconv, lconv
#include <cmath> // labs, isfinite, isnan, signbit
#include <cstddef> // size_t, ptrdiff_t
#include <cstdint> // uint8_t
#include <cstdio> // snprintf
#include <limits> // numeric_limits
#include <string> // string, char_traits
#include <type_traits> // is_same
#include <utility> // move
#include <nlohmann/detail/conversions/to_chars.hpp>
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/output/binary_writer.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
{
namespace detail
{
///////////////////
// serialization //
///////////////////
/// how to treat decoding errors
enum class error_handler_t
{
strict, ///< throw a type_error exception in case of invalid UTF-8
replace, ///< replace invalid UTF-8 sequences with U+FFFD
ignore ///< ignore invalid UTF-8 sequences
};
template<typename BasicJsonType>
class serializer
{
using string_t = typename BasicJsonType::string_t;
using number_float_t = typename BasicJsonType::number_float_t;
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
using binary_char_t = typename BasicJsonType::binary_t::value_type;
static constexpr std::uint8_t UTF8_ACCEPT = 0;
static constexpr std::uint8_t UTF8_REJECT = 1;
public:
/*!
@param[in] s output stream to serialize to
@param[in] ichar indentation character to use
@param[in] error_handler_ how to react on decoding errors
*/
serializer(output_adapter_t<char> s, const char ichar,
error_handler_t error_handler_ = error_handler_t::strict)
: o(std::move(s))
, loc(std::localeconv())
, thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
, decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
, indent_char(ichar)
, indent_string(512, indent_char)
, error_handler(error_handler_)
{}
// delete because of pointer members
serializer(const serializer&) = delete;
serializer& operator=(const serializer&) = delete;
serializer(serializer&&) = delete;
serializer& operator=(serializer&&) = delete;
~serializer() = default;
/*!
@brief internal implementation of the serialization function
This function is called by the public member function dump and organizes
the serialization internally. The indentation level is propagated as
additional parameter. In case of arrays and objects, the function is
called recursively.
- strings and object keys are escaped using `escape_string()`
- integer numbers are converted implicitly via `operator<<`
- floating-point numbers are converted to a string using `"%g"` format
- binary values are serialized as objects containing the subtype and the
byte array
@param[in] val value to serialize
@param[in] pretty_print whether the output shall be pretty-printed
@param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
in the output are escaped with `\uXXXX` sequences, and the result consists
of ASCII characters only.
@param[in] indent_step the indent level
@param[in] current_indent the current indent level (only used internally)
*/
void dump(const BasicJsonType& val,
const bool pretty_print,
const bool ensure_ascii,
const unsigned int indent_step,
const unsigned int current_indent = 0)
{
switch (val.m_type)
{
case value_t::object:
{
if (val.m_value.object->empty())
{
o->write_characters("{}", 2);
return;
}
if (pretty_print)
{
o->write_characters("{\n", 2);
// variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{
indent_string.resize(indent_string.size() * 2, ' ');
}
// first n-1 elements
auto i = val.m_value.object->cbegin();
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
{
o->write_characters(indent_string.c_str(), new_indent);
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent);
o->write_characters(",\n", 2);
}
// last element
JSON_ASSERT(i != val.m_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);
o->write_characters("\": ", 3);
dump(i->second, true, ensure_ascii, indent_step, new_indent);
o->write_character('\n');
o->write_characters(indent_string.c_str(), current_indent);
o->write_character('}');
}
else
{
o->write_character('{');
// first n-1 elements
auto i = val.m_value.object->cbegin();
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
{
o->write_character('\"');
dump_escaped(i->first, ensure_ascii);
o->write_characters("\":", 2);
dump(i->second, false, ensure_ascii, indent_step, current_indent);
o->write_character(',');
}
// last element
JSON_ASSERT(i != val.m_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);
dump(i->second, false, ensure_ascii, indent_step, current_indent);
o->write_character('}');
}
return;
}
case value_t::array:
{
if (val.m_value.array->empty())
{
o->write_characters("[]", 2);
return;
}
if (pretty_print)
{
o->write_characters("[\n", 2);
// variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{
indent_string.resize(indent_string.size() * 2, ' ');
}
// first n-1 elements
for (auto i = val.m_value.array->cbegin();
i != val.m_value.array->cend() - 1; ++i)
{
o->write_characters(indent_string.c_str(), new_indent);
dump(*i, true, ensure_ascii, indent_step, new_indent);
o->write_characters(",\n", 2);
}
// last element
JSON_ASSERT(!val.m_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);
o->write_character('\n');
o->write_characters(indent_string.c_str(), current_indent);
o->write_character(']');
}
else
{
o->write_character('[');
// first n-1 elements
for (auto i = val.m_value.array->cbegin();
i != val.m_value.array->cend() - 1; ++i)
{
dump(*i, false, ensure_ascii, indent_step, current_indent);
o->write_character(',');
}
// last element
JSON_ASSERT(!val.m_value.array->empty());
dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
o->write_character(']');
}
return;
}
case value_t::string:
{
o->write_character('\"');
dump_escaped(*val.m_value.string, ensure_ascii);
o->write_character('\"');
return;
}
case value_t::binary:
{
if (pretty_print)
{
o->write_characters("{\n", 2);
// variable to hold indentation for recursive calls
const auto new_indent = current_indent + indent_step;
if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
{
indent_string.resize(indent_string.size() * 2, ' ');
}
o->write_characters(indent_string.c_str(), new_indent);
o->write_characters("\"bytes\": [", 10);
if (!val.m_value.binary->empty())
{
for (auto i = val.m_value.binary->cbegin();
i != val.m_value.binary->cend() - 1; ++i)
{
dump_integer(*i);
o->write_characters(", ", 2);
}
dump_integer(val.m_value.binary->back());
}
o->write_characters("],\n", 3);
o->write_characters(indent_string.c_str(), new_indent);
o->write_characters("\"subtype\": ", 11);
if (val.m_value.binary->has_subtype())
{
dump_integer(val.m_value.binary->subtype());
}
else
{
o->write_characters("null", 4);
}
o->write_character('\n');
o->write_characters(indent_string.c_str(), current_indent);
o->write_character('}');
}
else
{
o->write_characters("{\"bytes\":[", 10);
if (!val.m_value.binary->empty())
{
for (auto i = val.m_value.binary->cbegin();
i != val.m_value.binary->cend() - 1; ++i)
{
dump_integer(*i);
o->write_character(',');
}
dump_integer(val.m_value.binary->back());
}
o->write_characters("],\"subtype\":", 12);
if (val.m_value.binary->has_subtype())
{
dump_integer(val.m_value.binary->subtype());
o->write_character('}');
}
else
{
o->write_characters("null}", 5);
}
}
return;
}
case value_t::boolean:
{
if (val.m_value.boolean)
{
o->write_characters("true", 4);
}
else
{
o->write_characters("false", 5);
}
return;
}
case value_t::number_integer:
{
dump_integer(val.m_value.number_integer);
return;
}
case value_t::number_unsigned:
{
dump_integer(val.m_value.number_unsigned);
return;
}
case value_t::number_float:
{
dump_float(val.m_value.number_float);
return;
}
case value_t::discarded:
{
o->write_characters("<discarded>", 11);
return;
}
case value_t::null:
{
o->write_characters("null", 4);
return;
}
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // LCOV_EXCL_LINE
}
}
private:
/*!
@brief dump escaped string
Escape a string by replacing certain special characters by a sequence of an
escape character (backslash) and another character and other control
characters by a sequence of "\u" followed by a four-digit hex
representation. The escaped string is written to output stream @a o.
@param[in] s the string to escape
@param[in] ensure_ascii whether to escape non-ASCII characters with
\uXXXX sequences
@complexity Linear in the length of string @a s.
*/
void dump_escaped(const string_t& s, const bool ensure_ascii)
{
std::uint32_t codepoint;
std::uint8_t state = UTF8_ACCEPT;
std::size_t bytes = 0; // number of bytes written to string_buffer
// number of bytes written at the point of the last valid byte
std::size_t bytes_after_last_accept = 0;
std::size_t undumped_chars = 0;
for (std::size_t i = 0; i < s.size(); ++i)
{
const auto byte = static_cast<uint8_t>(s[i]);
switch (decode(state, codepoint, byte))
{
case UTF8_ACCEPT: // decode found a new code point
{
switch (codepoint)
{
case 0x08: // backspace
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'b';
break;
}
case 0x09: // horizontal tab
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 't';
break;
}
case 0x0A: // newline
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'n';
break;
}
case 0x0C: // formfeed
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'f';
break;
}
case 0x0D: // carriage return
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'r';
break;
}
case 0x22: // quotation mark
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = '\"';
break;
}
case 0x5C: // reverse solidus
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = '\\';
break;
}
default:
{
// escape control characters (0x00..0x1F) or, if
// ensure_ascii parameter is used, non-ASCII characters
if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
{
if (codepoint <= 0xFFFF)
{
(std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
static_cast<std::uint16_t>(codepoint));
bytes += 6;
}
else
{
(std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
bytes += 12;
}
}
else
{
// copy byte to buffer (all previous bytes
// been copied have in default case above)
string_buffer[bytes++] = s[i];
}
break;
}
}
// write buffer and reset index; there must be 13 bytes
// left, as this is the maximal number of bytes to be
// written ("\uxxxx\uxxxx\0") for one code point
if (string_buffer.size() - bytes < 13)
{
o->write_characters(string_buffer.data(), bytes);
bytes = 0;
}
// remember the byte position of this accept
bytes_after_last_accept = bytes;
undumped_chars = 0;
break;
}
case UTF8_REJECT: // decode found invalid UTF-8 byte
{
switch (error_handler)
{
case error_handler_t::strict:
{
std::string sn(3, '\0');
(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));
}
case error_handler_t::ignore:
case error_handler_t::replace:
{
// in case we saw this character the first time, we
// would like to read it again, because the byte
// may be OK for itself, but just not OK for the
// previous sequence
if (undumped_chars > 0)
{
--i;
}
// reset length buffer to the last accepted index;
// thus removing/ignoring the invalid characters
bytes = bytes_after_last_accept;
if (error_handler == error_handler_t::replace)
{
// add a replacement character
if (ensure_ascii)
{
string_buffer[bytes++] = '\\';
string_buffer[bytes++] = 'u';
string_buffer[bytes++] = 'f';
string_buffer[bytes++] = 'f';
string_buffer[bytes++] = 'f';
string_buffer[bytes++] = 'd';
}
else
{
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
}
// write buffer and reset index; there must be 13 bytes
// left, as this is the maximal number of bytes to be
// written ("\uxxxx\uxxxx\0") for one code point
if (string_buffer.size() - bytes < 13)
{
o->write_characters(string_buffer.data(), bytes);
bytes = 0;
}
bytes_after_last_accept = bytes;
}
undumped_chars = 0;
// continue processing the string
state = UTF8_ACCEPT;
break;
}
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // LCOV_EXCL_LINE
}
break;
}
default: // decode found yet incomplete multi-byte code point
{
if (!ensure_ascii)
{
// code point will not be escaped - copy byte to buffer
string_buffer[bytes++] = s[i];
}
++undumped_chars;
break;
}
}
}
// we finished processing the string
if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
{
// write buffer
if (bytes > 0)
{
o->write_characters(string_buffer.data(), bytes);
}
}
else
{
// we finish reading, but do not accept: string was incomplete
switch (error_handler)
{
case error_handler_t::strict:
{
std::string sn(3, '\0');
(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));
}
case error_handler_t::ignore:
{
// write all accepted bytes
o->write_characters(string_buffer.data(), bytes_after_last_accept);
break;
}
case error_handler_t::replace:
{
// write all accepted bytes
o->write_characters(string_buffer.data(), bytes_after_last_accept);
// add a replacement character
if (ensure_ascii)
{
o->write_characters("\\ufffd", 6);
}
else
{
o->write_characters("\xEF\xBF\xBD", 3);
}
break;
}
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // LCOV_EXCL_LINE
}
}
}
/*!
@brief count digits
Count the number of decimal (base 10) digits for an input unsigned integer.
@param[in] x unsigned integer number to count its digits
@return number of decimal digits
*/
inline unsigned int count_digits(number_unsigned_t x) noexcept
{
unsigned int n_digits = 1;
for (;;)
{
if (x < 10)
{
return n_digits;
}
if (x < 100)
{
return n_digits + 1;
}
if (x < 1000)
{
return n_digits + 2;
}
if (x < 10000)
{
return n_digits + 3;
}
x = x / 10000u;
n_digits += 4;
}
}
/*!
@brief dump an integer
Dump a given integer to output stream @a o. Works internally with
@a number_buffer.
@param[in] x integer number (signed or unsigned) to dump
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
*/
template < typename NumberType, detail::enable_if_t <
std::is_same<NumberType, number_unsigned_t>::value ||
std::is_same<NumberType, number_integer_t>::value ||
std::is_same<NumberType, binary_char_t>::value,
int > = 0 >
void dump_integer(NumberType x)
{
static constexpr std::array<std::array<char, 2>, 100> digits_to_99
{
{
{{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},
{{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},
{{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},
{{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},
{{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},
{{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},
{{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},
{{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},
{{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},
{{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},
}
};
// special case for "0"
if (x == 0)
{
o->write_character('0');
return;
}
// use a pointer to fill the buffer
auto buffer_ptr = number_buffer.begin();
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;
if (is_negative)
{
*buffer_ptr = '-';
abs_value = remove_sign(static_cast<number_integer_t>(x));
// account one more byte for the minus sign
n_chars = 1 + count_digits(abs_value);
}
else
{
abs_value = static_cast<number_unsigned_t>(x);
n_chars = count_digits(abs_value);
}
// spare 1 byte for '\0'
JSON_ASSERT(n_chars < number_buffer.size() - 1);
// jump to the end to generate the string from backward
// so we later avoid reversing the result
buffer_ptr += n_chars;
// Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
// See: https://www.youtube.com/watch?v=o4-CwDo2zpg
while (abs_value >= 100)
{
const auto digits_index = static_cast<unsigned>((abs_value % 100));
abs_value /= 100;
*(--buffer_ptr) = digits_to_99[digits_index][1];
*(--buffer_ptr) = digits_to_99[digits_index][0];
}
if (abs_value >= 10)
{
const auto digits_index = static_cast<unsigned>(abs_value);
*(--buffer_ptr) = digits_to_99[digits_index][1];
*(--buffer_ptr) = digits_to_99[digits_index][0];
}
else
{
*(--buffer_ptr) = static_cast<char>('0' + abs_value);
}
o->write_characters(number_buffer.data(), n_chars);
}
/*!
@brief dump a floating-point number
Dump a given floating-point number to output stream @a o. Works internally
with @a number_buffer.
@param[in] x floating-point number to dump
*/
void dump_float(number_float_t x)
{
// NaN / inf
if (!std::isfinite(x))
{
o->write_characters("null", 4);
return;
}
// If number_float_t is an IEEE-754 single or double precision number,
// use the Grisu2 algorithm to produce short numbers which are
// guaranteed to round-trip, using strtof and strtod, resp.
//
// NB: The test below works if <long double> == <double>.
static constexpr bool is_ieee_single_or_double
= (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||
(std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);
dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
}
void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
{
char* begin = number_buffer.data();
char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
o->write_characters(begin, static_cast<size_t>(end - begin));
}
void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
{
// get number of digits for a float -> text -> float round-trip
static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
// the actual conversion
std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
// negative value indicates an error
JSON_ASSERT(len > 0);
// check if buffer was large enough
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
// erase thousands separator
if (thousands_sep != '\0')
{
const auto end = std::remove(number_buffer.begin(),
number_buffer.begin() + len, thousands_sep);
std::fill(end, number_buffer.end(), '\0');
JSON_ASSERT((end - number_buffer.begin()) <= len);
len = (end - number_buffer.begin());
}
// convert decimal point to '.'
if (decimal_point != '\0' && decimal_point != '.')
{
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
if (dec_pos != number_buffer.end())
{
*dec_pos = '.';
}
}
o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
// determine if need to append ".0"
const bool value_is_int_like =
std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
[](char c)
{
return c == '.' || c == 'e';
});
if (value_is_int_like)
{
o->write_characters(".0", 2);
}
}
/*!
@brief check whether a string is UTF-8 encoded
The function checks each byte of a string whether it is UTF-8 encoded. The
result of the check is stored in the @a state parameter. The function must
be called initially with state 0 (accept). State 1 means the string must
be rejected, because the current byte is not allowed. If the string is
completely processed, but the state is non-zero, the string ended
prematurely; that is, the last byte indicated more bytes should have
followed.
@param[in,out] state the state of the decoding
@param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
@param[in] byte next byte to decode
@return new state
@note The function has been edited: a std::array is used.
@copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
*/
static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
{
static const std::array<std::uint8_t, 400> utf8d =
{
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
}
};
const std::uint8_t type = utf8d[byte];
codep = (state != UTF8_ACCEPT)
? (byte & 0x3fu) | (codep << 6u)
: (0xFFu >> type) & (byte);
std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
JSON_ASSERT(index < 400);
state = utf8d[index];
return state;
}
/*
* Overload to make the compiler happy while it is instantiating
* dump_integer for number_unsigned_t.
* Must never be called.
*/
number_unsigned_t remove_sign(number_unsigned_t x)
{
JSON_ASSERT(false); // LCOV_EXCL_LINE
return x; // LCOV_EXCL_LINE
}
/*
* Helper function for dump_integer
*
* This function takes a negative signed integer and returns its absolute
* value as unsigned integer. The plus/minus shuffling is necessary as we can
* not directly remove the sign of an arbitrary signed integer as the
* absolute values of INT_MIN and INT_MAX are usually not the same. See
* #1708 for details.
*/
inline number_unsigned_t remove_sign(number_integer_t x) noexcept
{
JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)());
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
}
private:
/// the output of the serializer
output_adapter_t<char> o = nullptr;
/// a (hopefully) large enough character buffer
std::array<char, 64> number_buffer{{}};
/// the locale
const std::lconv* loc = nullptr;
/// the locale's thousand separator character
const char thousands_sep = '\0';
/// the locale's decimal point character
const char decimal_point = '\0';
/// string buffer
std::array<char, 512> string_buffer{{}};
/// the indentation character
const char indent_char;
/// the indentation string
string_t indent_string;
/// error_handler how to react on decoding errors
const error_handler_t error_handler;
};
} // namespace detail
} // namespace nlohmann

View File

@ -0,0 +1,81 @@
#pragma once
#include <array> // array
#include <cstddef> // size_t
#include <cstdint> // uint8_t
#include <string> // string
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() && r_index < order.size() && order[l_index] < order[r_index];
}
} // namespace detail
} // namespace nlohmann

8791
external/json/include/nlohmann/json.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,78 @@
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
#include <cstdint> // int64_t, uint64_t
#include <map> // map
#include <memory> // allocator
#include <string> // string
#include <vector> // vector
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@since version 1.0.0
*/
namespace nlohmann
{
/*!
@brief default JSONSerializer template argument
This serializer ignores the template arguments and uses ADL
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
for serialization.
*/
template<typename T = void, typename SFINAE = void>
struct adl_serializer;
template<template<typename U, typename V, typename... Args> class ObjectType =
std::map,
template<typename U, typename... Args> class ArrayType = std::vector,
class StringType = std::string, class BooleanType = bool,
class NumberIntegerType = std::int64_t,
class NumberUnsignedType = std::uint64_t,
class NumberFloatType = double,
template<typename U> class AllocatorType = std::allocator,
template<typename T, typename SFINAE = void> class JSONSerializer =
adl_serializer,
class BinaryType = std::vector<std::uint8_t>>
class basic_json;
/*!
@brief JSON Pointer
A JSON pointer defines a string syntax for identifying a specific value
within a JSON document. It can be used with functions `at` and
`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
@since version 2.0.0
*/
template<typename BasicJsonType>
class json_pointer;
/*!
@brief default JSON class
This type is the default specialization of the @ref basic_json class which
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_

View File

@ -0,0 +1,171 @@
#pragma once
#include <functional> // less
#include <memory> // allocator
#include <utility> // pair
#include <vector> // vector
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;
}
}
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;
}
}
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};
}
};
} // namespace nlohmann

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,143 @@
#undef JSON_HEDLEY_ALWAYS_INLINE
#undef JSON_HEDLEY_ARM_VERSION
#undef JSON_HEDLEY_ARM_VERSION_CHECK
#undef JSON_HEDLEY_ARRAY_PARAM
#undef JSON_HEDLEY_ASSUME
#undef JSON_HEDLEY_BEGIN_C_DECLS
#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
#undef JSON_HEDLEY_CLANG_HAS_BUILTIN
#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
#undef JSON_HEDLEY_CLANG_HAS_EXTENSION
#undef JSON_HEDLEY_CLANG_HAS_FEATURE
#undef JSON_HEDLEY_CLANG_HAS_WARNING
#undef JSON_HEDLEY_COMPCERT_VERSION
#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
#undef JSON_HEDLEY_CONCAT
#undef JSON_HEDLEY_CONCAT3
#undef JSON_HEDLEY_CONCAT3_EX
#undef JSON_HEDLEY_CONCAT_EX
#undef JSON_HEDLEY_CONST
#undef JSON_HEDLEY_CONSTEXPR
#undef JSON_HEDLEY_CONST_CAST
#undef JSON_HEDLEY_CPP_CAST
#undef JSON_HEDLEY_CRAY_VERSION
#undef JSON_HEDLEY_CRAY_VERSION_CHECK
#undef JSON_HEDLEY_C_DECL
#undef JSON_HEDLEY_DEPRECATED
#undef JSON_HEDLEY_DEPRECATED_FOR
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
#undef JSON_HEDLEY_DIAGNOSTIC_POP
#undef JSON_HEDLEY_DIAGNOSTIC_PUSH
#undef JSON_HEDLEY_DMC_VERSION
#undef JSON_HEDLEY_DMC_VERSION_CHECK
#undef JSON_HEDLEY_EMPTY_BASES
#undef JSON_HEDLEY_EMSCRIPTEN_VERSION
#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
#undef JSON_HEDLEY_END_C_DECLS
#undef JSON_HEDLEY_FLAGS
#undef JSON_HEDLEY_FLAGS_CAST
#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
#undef JSON_HEDLEY_GCC_HAS_BUILTIN
#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
#undef JSON_HEDLEY_GCC_HAS_EXTENSION
#undef JSON_HEDLEY_GCC_HAS_FEATURE
#undef JSON_HEDLEY_GCC_HAS_WARNING
#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
#undef JSON_HEDLEY_GCC_VERSION
#undef JSON_HEDLEY_GCC_VERSION_CHECK
#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
#undef JSON_HEDLEY_GNUC_HAS_BUILTIN
#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
#undef JSON_HEDLEY_GNUC_HAS_EXTENSION
#undef JSON_HEDLEY_GNUC_HAS_FEATURE
#undef JSON_HEDLEY_GNUC_HAS_WARNING
#undef JSON_HEDLEY_GNUC_VERSION
#undef JSON_HEDLEY_GNUC_VERSION_CHECK
#undef JSON_HEDLEY_HAS_ATTRIBUTE
#undef JSON_HEDLEY_HAS_BUILTIN
#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
#undef JSON_HEDLEY_HAS_EXTENSION
#undef JSON_HEDLEY_HAS_FEATURE
#undef JSON_HEDLEY_HAS_WARNING
#undef JSON_HEDLEY_IAR_VERSION
#undef JSON_HEDLEY_IAR_VERSION_CHECK
#undef JSON_HEDLEY_IBM_VERSION
#undef JSON_HEDLEY_IBM_VERSION_CHECK
#undef JSON_HEDLEY_IMPORT
#undef JSON_HEDLEY_INLINE
#undef JSON_HEDLEY_INTEL_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_MESSAGE
#undef JSON_HEDLEY_MSVC_VERSION
#undef JSON_HEDLEY_MSVC_VERSION_CHECK
#undef JSON_HEDLEY_NEVER_INLINE
#undef JSON_HEDLEY_NON_NULL
#undef JSON_HEDLEY_NO_ESCAPE
#undef JSON_HEDLEY_NO_RETURN
#undef JSON_HEDLEY_NO_THROW
#undef JSON_HEDLEY_NULL
#undef JSON_HEDLEY_PELLES_VERSION
#undef JSON_HEDLEY_PELLES_VERSION_CHECK
#undef JSON_HEDLEY_PGI_VERSION
#undef JSON_HEDLEY_PGI_VERSION_CHECK
#undef JSON_HEDLEY_PREDICT
#undef JSON_HEDLEY_PRINTF_FORMAT
#undef JSON_HEDLEY_PRIVATE
#undef JSON_HEDLEY_PUBLIC
#undef JSON_HEDLEY_PURE
#undef JSON_HEDLEY_REINTERPRET_CAST
#undef JSON_HEDLEY_REQUIRE
#undef JSON_HEDLEY_REQUIRE_CONSTEXPR
#undef JSON_HEDLEY_REQUIRE_MSG
#undef JSON_HEDLEY_RESTRICT
#undef JSON_HEDLEY_RETURNS_NON_NULL
#undef JSON_HEDLEY_SENTINEL
#undef JSON_HEDLEY_STATIC_ASSERT
#undef JSON_HEDLEY_STATIC_CAST
#undef JSON_HEDLEY_STRINGIFY
#undef JSON_HEDLEY_STRINGIFY_EX
#undef JSON_HEDLEY_SUNPRO_VERSION
#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
#undef JSON_HEDLEY_TINYC_VERSION
#undef JSON_HEDLEY_TINYC_VERSION_CHECK
#undef JSON_HEDLEY_TI_ARMCL_VERSION
#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
#undef JSON_HEDLEY_TI_CL2000_VERSION
#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
#undef JSON_HEDLEY_TI_CL430_VERSION
#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
#undef JSON_HEDLEY_TI_CL6X_VERSION
#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
#undef JSON_HEDLEY_TI_CL7X_VERSION
#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
#undef JSON_HEDLEY_TI_CLPRU_VERSION
#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
#undef JSON_HEDLEY_TI_VERSION
#undef JSON_HEDLEY_TI_VERSION_CHECK
#undef JSON_HEDLEY_UNAVAILABLE
#undef JSON_HEDLEY_UNLIKELY
#undef JSON_HEDLEY_UNPREDICTABLE
#undef JSON_HEDLEY_UNREACHABLE
#undef JSON_HEDLEY_UNREACHABLE_RETURN
#undef JSON_HEDLEY_VERSION
#undef JSON_HEDLEY_VERSION_DECODE_MAJOR
#undef JSON_HEDLEY_VERSION_DECODE_MINOR
#undef JSON_HEDLEY_VERSION_DECODE_REVISION
#undef JSON_HEDLEY_VERSION_ENCODE
#undef JSON_HEDLEY_WARNING
#undef JSON_HEDLEY_WARN_UNUSED_RESULT
#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
#undef JSON_HEDLEY_FALL_THROUGH

23
external/json/meson.build vendored Normal file
View File

@ -0,0 +1,23 @@
project('nlohmann_json',
'cpp',
version : '3.9.1',
license : 'MIT',
)
nlohmann_json_dep = declare_dependency(
include_directories: include_directories('single_include')
)
nlohmann_json_multiple_headers = declare_dependency(
include_directories: include_directories('include')
)
if not meson.is_subproject()
install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann')
pkgc = import('pkgconfig')
pkgc.generate(name: 'nlohmann_json',
version: meson.project_version(),
description: 'JSON for Modern C++'
)
endif

File diff suppressed because it is too large Load Diff

1
external/physfs vendored Submodule

@ -0,0 +1 @@
Subproject commit 6c0ad192e7fff5173ef86a78bb5ed3d7e9fa507c

201
external/soloud/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,201 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
project(soloud CXX)
set(CPP_FILES
#core
soloud/src/core/soloud.cpp
soloud/src/core/soloud_audiosource.cpp
soloud/src/core/soloud_bus.cpp
soloud/src/core/soloud_core_3d.cpp
soloud/src/core/soloud_core_basicops.cpp
soloud/src/core/soloud_core_faderops.cpp
soloud/src/core/soloud_core_filterops.cpp
soloud/src/core/soloud_core_getters.cpp
soloud/src/core/soloud_core_setters.cpp
soloud/src/core/soloud_core_voicegroup.cpp
soloud/src/core/soloud_core_voiceops.cpp
soloud/src/core/soloud_fader.cpp
soloud/src/core/soloud_fft.cpp
soloud/src/core/soloud_fft_lut.cpp
soloud/src/core/soloud_file.cpp
soloud/src/core/soloud_filter.cpp
soloud/src/core/soloud_misc.cpp
soloud/src/core/soloud_queue.cpp
soloud/src/core/soloud_thread.cpp
#backends
soloud/src/backend/alsa/soloud_alsa.cpp
soloud/src/backend/coreaudio/soloud_coreaudio.cpp
soloud/src/backend/null/soloud_null.cpp
#soloud/src/backend/openal/soloud_openal.cpp
#soloud/src/backend/openal/soloud_openal_dll.c
soloud/src/backend/opensles/soloud_opensles.cpp
soloud/src/backend/oss/soloud_oss.cpp
soloud/src/backend/portaudio/soloud_portaudio.cpp
soloud/src/backend/portaudio/soloud_portaudio_dll.c
#soloud/src/backend/sdl/soloud_sdl1.cpp
#soloud/src/backend/sdl/soloud_sdl1_dll.c
#soloud/src/backend/sdl/soloud_sdl2.cpp
#soloud/src/backend/sdl/soloud_sdl2_dll.c
#soloud/src/backend/sdl_static/soloud_sdl_static.cpp
soloud/src/backend/sdl2_static/soloud_sdl2_static.cpp
#soloud/src/backend/vita_homebrew/soloud_vita_homebrew.cpp
soloud/src/backend/wasapi/soloud_wasapi.cpp
soloud/src/backend/winmm/soloud_winmm.cpp
soloud/src/backend/xaudio2/soloud_xaudio2.cpp
#filter
soloud/src/filter/soloud_bassboostfilter.cpp
soloud/src/filter/soloud_biquadresonantfilter.cpp
soloud/src/filter/soloud_dcremovalfilter.cpp
soloud/src/filter/soloud_echofilter.cpp
soloud/src/filter/soloud_fftfilter.cpp
soloud/src/filter/soloud_freeverbfilter.cpp
soloud/src/filter/soloud_flangerfilter.cpp
soloud/src/filter/soloud_lofifilter.cpp
soloud/src/filter/soloud_robotizefilter.cpp
soloud/src/filter/soloud_waveshaperfilter.cpp
#sources
#monotone
soloud/src/audiosource/monotone/soloud_monotone.cpp
#noise
soloud/src/audiosource/noise/soloud_noise.cpp
#openmpt
soloud/src/audiosource/openmpt/soloud_openmpt.cpp
soloud/src/audiosource/openmpt/soloud_openmpt_dll.c
#sfxr
soloud/src/audiosource/sfxr/soloud_sfxr.cpp
#speech
soloud/src/audiosource/speech/darray.cpp
soloud/src/audiosource/speech/darray.h
#soloud/src/audiosource/speech/Elements.def
soloud/src/audiosource/speech/klatt.cpp
soloud/src/audiosource/speech/klatt.h
soloud/src/audiosource/speech/resonator.cpp
soloud/src/audiosource/speech/resonator.h
soloud/src/audiosource/speech/soloud_speech.cpp
soloud/src/audiosource/speech/tts.cpp
soloud/src/audiosource/speech/tts.h
#tedsid
soloud/src/audiosource/tedsid/sid.cpp
soloud/src/audiosource/tedsid/sid.h
soloud/src/audiosource/tedsid/soloud_tedsid.cpp
soloud/src/audiosource/tedsid/ted.cpp
soloud/src/audiosource/tedsid/ted.h
#vic
soloud/src/audiosource/vic/soloud_vic.cpp
#vizsn
soloud/src/audiosource/vizsn/soloud_vizsn.cpp
#wav
soloud/src/audiosource/wav/soloud_wav.cpp
soloud/src/audiosource/wav/soloud_wavstream.cpp
# TODO: move to extra lib
soloud/src/audiosource/wav/dr_flac.h
soloud/src/audiosource/wav/dr_impl.cpp
soloud/src/audiosource/wav/dr_mp3.h
soloud/src/audiosource/wav/dr_wav.h
soloud/src/audiosource/wav/stb_vorbis.c
soloud/src/audiosource/wav/stb_vorbis.h
)
set(HPP_FILES
#c_api
#soloud/include/soloud_c.h
#core
soloud/include/soloud.h
soloud/include/soloud_internal.h
soloud/include/soloud_audiosource.h
soloud/include/soloud_bus.h
soloud/include/soloud_error.h
soloud/include/soloud_fader.h
soloud/include/soloud_fft.h
soloud/include/soloud_file.h
soloud/include/soloud_file_hack_off.h
soloud/include/soloud_file_hack_on.h
soloud/include/soloud_filter.h
soloud/include/soloud_queue.h
soloud/include/soloud_thread.h
#filter
soloud/include/soloud_bassboostfilter.h
soloud/include/soloud_biquadresonantfilter.h
soloud/include/soloud_dcremovalfilter.h
soloud/include/soloud_echofilter.h
soloud/include/soloud_fftfilter.h
soloud/include/soloud_flangerfilter.h
soloud/include/soloud_lofifilter.h
soloud/include/soloud_robotizefilter.h
soloud/include/soloud_waveshaperfilter.h
#sources
soloud/include/soloud_wav.h
soloud/include/soloud_wavstream.h
soloud/include/soloud_monotone.h
soloud/include/soloud_openmpt.h
soloud/include/soloud_sfxr.h
soloud/include/soloud_speech.h
soloud/include/soloud_tedsid.h
soloud/include/soloud_vic.h
soloud/include/soloud_vizsn.h
)
add_library(soloud ${CPP_FILES} ${HPP_FILES})
target_compile_definitions(soloud PRIVATE WITH_NULL)
#target_compile_definitions(soloud PRIVATE WITH_SDL2)
target_compile_definitions(soloud PRIVATE WITH_SDL2_STATIC)
target_include_directories(soloud PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/soloud/include")
#target_include_directories(soloud PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/imgui")
#if not android or emscripten
target_include_directories(soloud PUBLIC "${SDL2_INCLUDE_DIR}")
target_link_libraries(soloud ${SDL2_LIBRARY})
#endif
#if android
#target_link_libraries(soloud SDL)
#endif
if(EMSCRIPTEN)
set_target_properties(soloud PROPERTIES COMPILE_FLAGS "-s USE_SDL=2")
set_target_properties(soloud PROPERTIES LINK_FLAGS "-s USE_SDL=2")
endif()
# TODO: remove
if(UNIX)
target_link_libraries(soloud
-ldl
)
endif()
add_executable(soloud_demo_simplest EXCLUDE_FROM_ALL soloud/demos/simplest/main.cpp)
target_link_libraries(soloud_demo_simplest
soloud
)
add_executable(soloud_demo_enumerate EXCLUDE_FROM_ALL ./soloud/demos/enumerate/main.cpp)
target_link_libraries(soloud_demo_enumerate
soloud
)
add_executable(soloud_demo_welcome EXCLUDE_FROM_ALL ./soloud/demos/welcome/main.cpp)
target_link_libraries(soloud_demo_welcome
soloud
)

1
external/soloud/soloud vendored Submodule

@ -0,0 +1 @@
Subproject commit 4d72336a8855f3f421f95ec75cda9062da3fe7eb

1
external/spdlog vendored Submodule

@ -0,0 +1 @@
Subproject commit 22a169bc319ac06948e7ee0be6b9b0ac81386604

13
external/stb/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
project(stb CXX)
add_library(stb INTERFACE)
target_include_directories(stb INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
add_library(stb_image "stb/stb_image.h" "stb_image.cpp")
target_link_libraries(stb_image stb)
add_library(stb_perlin "stb/stb_perlin.h" "stb_perlin.cpp")
target_link_libraries(stb_perlin stb)

1
external/stb/stb vendored Submodule

@ -0,0 +1 @@
Subproject commit b42009b3b9d4ca35bc703f5310eedc74f584be58

3
external/stb/stb_image.cpp vendored Normal file
View File

@ -0,0 +1,3 @@
#define STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"

3
external/stb/stb_perlin.cpp vendored Normal file
View File

@ -0,0 +1,3 @@
#define STB_PERLIN_IMPLEMENTATION
#include "stb/stb_perlin.h"

28
external/tracy/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
add_library(tracy_client
"${CMAKE_CURRENT_LIST_DIR}/tracy/Tracy.hpp"
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyLua.hpp"
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyVulkan.hpp"
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyOpenGL.hpp"
"${CMAKE_CURRENT_LIST_DIR}/tracy/TracyClient.cpp"
)
target_compile_features(tracy_client PUBLIC cxx_std_17)
if(TRACY_ENABLE)
target_compile_definitions(tracy_client PUBLIC TRACY_ENABLE)
#target_compile_definitions(tracy_client PUBLIC TRACY_NO_SYSTEM_TRACING)
message("Enabled TRACY")
endif()
target_include_directories(tracy_client PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
#target_link_libraries(tracy_client
#)
if(UNIX)
target_link_libraries(tracy_client dl)
endif()

1
external/tracy/tracy vendored Submodule

@ -0,0 +1 @@
Subproject commit 54909485678ed19cd67ce3644352e40e34a833e9

23
framework/CMakeLists.txt Normal file
View File

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.2)
project(framework CXX)
add_subdirectory(engine)
add_subdirectory(logger)
add_subdirectory(resource_manager)
add_subdirectory(common_components)
add_subdirectory(std_utils)
add_subdirectory(screen_director)
add_subdirectory(filesystem)
add_subdirectory(simple_scene)
if(NOT MM_HEADLESS)
add_subdirectory(sdl_service)
add_subdirectory(simple_sdl_renderer)
add_subdirectory(opengl_renderer)
add_subdirectory(opengl_primitives)
add_subdirectory(input)
add_subdirectory(sound)
add_subdirectory(imgui)
add_subdirectory(tilemap)
endif()

View File

@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.2)
project(common_components CXX)
add_library(common_components INTERFACE)
target_include_directories(common_components INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(common_components INTERFACE
entt
glm
)
##########################
add_library(common_components_serialize_json INTERFACE)
target_include_directories(common_components_serialize_json INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(common_components_serialize_json INTERFACE
common_components
nlohmann_json::nlohmann_json
)
if(BUILD_TESTING)
add_subdirectory(test)
endif()

View File

@ -0,0 +1,10 @@
#pragma once
#include <glm/vec4.hpp>
namespace MM::Components {
struct Color {
glm::vec4 color {1.f, 1.f, 1.f, 1.f};
};
}

View File

@ -0,0 +1,11 @@
#pragma once
#include <string>
namespace MM::Components {
struct Name {
static const size_t max_str_len = 64;
std::string str;
};
}

Some files were not shown because too many files have changed in this diff Show More