diff --git a/ext/ghoul b/ext/ghoul index 307458fea7..bc86cd17f9 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 307458fea7e07758efe013d97e810836e0eb62d2 +Subproject commit bc86cd17f9e9a5323e7231257cd55c61b51a18cf diff --git a/modules/iswa/ext/json.h b/include/openspace/json.h similarity index 87% rename from modules/iswa/ext/json.h rename to include/openspace/json.h index eddbb50955..4af4d7a02e 100644 --- a/modules/iswa/ext/json.h +++ b/include/openspace/json.h @@ -1,8 +1,9 @@ /***************************************************************************************** * * - * OpenSpace * + * GHOUL * + * General Helpful Open Utility Library * * * - * Copyright (c) 2014-2018 * + * Copyright (c) 2012-2018 * * * * 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 * @@ -22,8 +23,8 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __OPENSPACE_MODULE_KAMELEONVOLUME___JSON___H__ -#define __OPENSPACE_MODULE_KAMELEONVOLUME___JSON___H__ +#ifndef __OPENSPACE_CORE___JSON___H__ +#define __OPENSPACE_CORE___JSON___H__ #ifdef WIN32 #pragma warning (push) @@ -36,8 +37,7 @@ #pragma GCC diagnostic ignored "-Wuseless-cast" #endif // __GNUC__ - -#include +#include #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop @@ -47,4 +47,4 @@ #pragma warning (pop) #endif // WIN32 -#endif // __OPENSPACE_MODULE_KAMELEONVOLUME___JSON___H__ +#endif // __OPENSPACE_CORE___JSON___H__ diff --git a/include/openspace/util/spicemanager.h b/include/openspace/util/spicemanager.h index 6b5b96a9bd..ada7430d93 100644 --- a/include/openspace/util/spicemanager.h +++ b/include/openspace/util/spicemanager.h @@ -364,7 +364,7 @@ public: * succeeds iff \p body is the name of a valid body, \p value is a value associated * with the body, and the value consists of three \c double values. If all conditions * are true, the value is retrieved using the method \c bodvrd_c and stored in \p v. - * + * * \param body The name of the body whose value should be retrieved or the NAIF ID of * this body * \param value The value that should be retrieved, this value is case-sensitive diff --git a/include/openspace/util/syncdata.inl b/include/openspace/util/syncdata.inl index a579090861..b0f099ae98 100644 --- a/include/openspace/util/syncdata.inl +++ b/include/openspace/util/syncdata.inl @@ -27,7 +27,7 @@ namespace openspace { template -SyncData::SyncData(const T& val) : data(val) {}; +SyncData::SyncData(const T& val) : data(val) {} template SyncData::SyncData(const SyncData& o) : data(o.data) {} @@ -70,6 +70,6 @@ void SyncData::postSync(bool isMaster) { data = doubleBufferedData; _mutex.unlock(); } -}; +} } // namespace openspace diff --git a/modules/atmosphere/rendering/atmospheredeferredcaster.cpp b/modules/atmosphere/rendering/atmospheredeferredcaster.cpp index 7daa08ef0e..c5ebd7c360 100644 --- a/modules/atmosphere/rendering/atmospheredeferredcaster.cpp +++ b/modules/atmosphere/rendering/atmospheredeferredcaster.cpp @@ -191,7 +191,10 @@ void AtmosphereDeferredcaster::preRaycast(const RenderData& renderData, _modelTransform * glm::dvec4(0.0, 0.0, 0.0, 1.0) ); - const double distance = glm::distance(tPlanetPosWorld, renderData.camera.eyePositionVec3()); + const double distance = glm::distance( + tPlanetPosWorld, + renderData.camera.eyePositionVec3() + ); // Radius is in KM const double scaledRadius = glm::length( @@ -252,7 +255,7 @@ void AtmosphereDeferredcaster::preRaycast(const RenderData& renderData, // program.setUniform(_uniformCache2.dInverseSgctProjectionToModelTransformMatrix, // inverseWholeMatrixPipeline); // -// program.setUniform(_uniformCache2.dInverseSGCTEyeToTmpRotTransformMatrix, +// program.setUniform(_uniformCache2.dInverseSGCTEyeToTmpRotTransformMatrix, // dInverseCameraRotationToSgctEyeTransform); // // program.setUniform(_uniformCache2.dCampos, renderData.camera.positionVec3()); @@ -1459,50 +1462,61 @@ void AtmosphereDeferredcaster::checkFrameBufferState( LERROR("Framework not built. " + codePosition); GLenum fbErr = glCheckFramebufferStatus(GL_FRAMEBUFFER); switch (fbErr) { - case GL_FRAMEBUFFER_UNDEFINED: - LERROR("Indefined framebuffer."); - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - LERROR("Incomplete, missing attachement."); - break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - LERROR("Framebuffer doesn't have at least one image attached to it."); - break; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - LERROR("Returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is " - "GL_NONE for any color attachment point(s) named by GL_DRAW_BUFFERi."); - break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - LERROR("Returned if GL_READ_BUFFER is not GL_NONE and the value of " - "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color attachment " - "point named by GL_READ_BUFFER."); - break; - case GL_FRAMEBUFFER_UNSUPPORTED: - LERROR("Returned if the combination of internal formats of the attached " - "images violates an implementation - dependent set of restrictions."); - break; - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: - LERROR("Returned if the value of GL_RENDERBUFFE_r_samples is not the same " - "for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES is " - "the not same for all attached textures; or , if the attached images are " - "a mix of renderbuffers and textures, the value of " - "GL_RENDERBUFFE_r_samples does not match the value of " - "GL_TEXTURE_SAMPLES."); - LERROR("Returned if the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not " - "the same for all attached textures; or , if the attached images are a " - "mix of renderbuffers and textures, the value of " - "GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached " - "textures."); - break; - case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: - LERROR("Returned if any framebuffer attachment is layered, and any populated " - "attachment is not layered, or if all populated color attachments are " - "not from textures of the same target."); - break; - default: - LDEBUG("No error found checking framebuffer: " + codePosition); - break; - } + case GL_FRAMEBUFFER_UNDEFINED: + LERROR("Indefined framebuffer."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + LERROR("Incomplete, missing attachement."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + LERROR("Framebuffer doesn't have at least one image attached to it."); + break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + LERROR( + "Returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is " + "GL_NONE for any color attachment point(s) named by GL_DRAW_BUFFERi." + ); + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + LERROR( + "Returned if GL_READ_BUFFER is not GL_NONE and the value of " + "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color " + "attachment point named by GL_READ_BUFFER."); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + LERROR( + "Returned if the combination of internal formats of the attached " + "images violates an implementation - dependent set of restrictions." + ); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + LERROR( + "Returned if the value of GL_RENDERBUFFE_r_samples is not the same " + "for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES " + "is the not same for all attached textures; or , if the attached " + "images are a mix of renderbuffers and textures, the value of " + "GL_RENDERBUFFE_r_samples does not match the value of " + "GL_TEXTURE_SAMPLES." + ); + LERROR( + "Returned if the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not " + "the same for all attached textures; or , if the attached images are " + "a mix of renderbuffers and textures, the value of " + "GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached " + "textures." + ); + break; + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: + LERROR( + "Returned if any framebuffer attachment is layered, and any " + "populated attachment is not layered, or if all populated color " + "attachments are not from textures of the same target." + ); + break; + default: + LDEBUG("No error found checking framebuffer: " + codePosition); + break; + } } } diff --git a/modules/base/dashboard/dashboarditempropertyvalue.cpp b/modules/base/dashboard/dashboarditempropertyvalue.cpp index 63f2ace82a..5e133cd90e 100644 --- a/modules/base/dashboard/dashboarditempropertyvalue.cpp +++ b/modules/base/dashboard/dashboarditempropertyvalue.cpp @@ -106,7 +106,8 @@ documentation::Documentation DashboardItemPropertyValue::Documentation() { }; } -DashboardItemPropertyValue::DashboardItemPropertyValue(const ghoul::Dictionary& dictionary) +DashboardItemPropertyValue::DashboardItemPropertyValue( + const ghoul::Dictionary& dictionary) : DashboardItem(dictionary) , _fontName(FontNameInfo, KeyFontMono) , _fontSize(FontSizeInfo, DefaultFontSize, 6.f, 144.f, 1.f) diff --git a/modules/base/rendering/renderableplane.cpp b/modules/base/rendering/renderableplane.cpp index 60abcb2cbd..5dc41e9405 100644 --- a/modules/base/rendering/renderableplane.cpp +++ b/modules/base/rendering/renderableplane.cpp @@ -200,7 +200,7 @@ void RenderablePlane::render(const RenderData& data, RendererTasks&) { glm::dvec3 objectPositionWorld = glm::dvec3( glm::translate( - glm::dmat4(1.0), + glm::dmat4(1.0), data.modelTransform.translation) * glm::dvec4(0.0, 0.0, 0.0, 1.0) ); diff --git a/modules/base/rotation/fixedrotation.cpp b/modules/base/rotation/fixedrotation.cpp index fe3ca97199..772d3785b4 100644 --- a/modules/base/rotation/fixedrotation.cpp +++ b/modules/base/rotation/fixedrotation.cpp @@ -100,7 +100,8 @@ namespace { "stay fixed to the current position of that object." }; - constexpr const openspace::properties::Property::PropertyInfo XAxisInvertObjectInfo = { + constexpr const openspace::properties::Property::PropertyInfo XAxisInvertObjectInfo = + { "xAxis-InvertObject", "xAxis: Invert Object Point Direction", "If this value is set to 'true', and the type is set to 'Object', the inverse of " @@ -108,7 +109,8 @@ namespace { "referenced object." }; - constexpr const openspace::properties::Property::PropertyInfo YAxisInvertObjectInfo = { + constexpr const openspace::properties::Property::PropertyInfo YAxisInvertObjectInfo = + { "yAxis-InvertObject", "yAxis: Invert Object Point Direction", "If this value is set to 'true', and the type is set to 'Object', the inverse of " @@ -116,7 +118,8 @@ namespace { "referenced object." }; - constexpr const openspace::properties::Property::PropertyInfo ZAxisInvertObjectInfo = { + constexpr const openspace::properties::Property::PropertyInfo ZAxisInvertObjectInfo = + { "zAxis-InvertObject", "zAxis: Invert Object Point Direction", "If this value is set to 'true', and the type is set to 'Object', the inverse of " @@ -145,7 +148,9 @@ namespace { "rotation." }; - constexpr const openspace::properties::Property::PropertyInfo XAxisOrthogonalVectorInfo = { + constexpr const openspace::properties::Property::PropertyInfo + XAxisOrthogonalVectorInfo = + { "xAxis-Orthogonal", "xAxis: Vector is orthogonal", "This value determines whether the vector specified is used directly, or whether " @@ -153,7 +158,9 @@ namespace { "construct an orthogonal vector instead." }; - constexpr const openspace::properties::Property::PropertyInfo YAxisOrthogonalVectorInfo = { + constexpr const openspace::properties::Property::PropertyInfo + YAxisOrthogonalVectorInfo = + { "yAxis-Orthogonal", "yAxis: Vector is orthogonal", "This value determines whether the vector specified is used directly, or whether " @@ -161,7 +168,9 @@ namespace { "construct an orthogonal vector instead." }; - constexpr const openspace::properties::Property::PropertyInfo ZAxisOrthogonalVectorInfo = { + constexpr const openspace::properties::Property::PropertyInfo + ZAxisOrthogonalVectorInfo = + { "zAxis-Orthogonal", "zAxis: Vector is orthogonal", "This value determines whether the vector specified is used directly, or whether " diff --git a/modules/fieldlinessequence/util/fieldlinesstate.cpp b/modules/fieldlinessequence/util/fieldlinesstate.cpp index 8f4aaa3065..cce890eb6e 100644 --- a/modules/fieldlinessequence/util/fieldlinesstate.cpp +++ b/modules/fieldlinessequence/util/fieldlinesstate.cpp @@ -24,10 +24,10 @@ #include +#include #include #include #include -#include #include namespace { diff --git a/modules/globebrowsing/CMakeLists.txt b/modules/globebrowsing/CMakeLists.txt index bf0b1a95f4..91d80d1ad2 100644 --- a/modules/globebrowsing/CMakeLists.txt +++ b/modules/globebrowsing/CMakeLists.txt @@ -42,7 +42,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/chunk/culling/frustumculler.h ${CMAKE_CURRENT_SOURCE_DIR}/chunk/culling/horizonculler.h - ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboardglobelocation.h + ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemglobelocation.h ${CMAKE_CURRENT_SOURCE_DIR}/geometry/aabb.h ${CMAKE_CURRENT_SOURCE_DIR}/geometry/angle.h @@ -138,7 +138,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/chunk/culling/frustumculler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/chunk/culling/horizonculler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboardglobelocation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemglobelocation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/geometry/aabb.cpp ${CMAKE_CURRENT_SOURCE_DIR}/geometry/ellipsoid.cpp diff --git a/modules/globebrowsing/dashboard/dashboardglobelocation.cpp b/modules/globebrowsing/dashboard/dashboarditemglobelocation.cpp similarity index 97% rename from modules/globebrowsing/dashboard/dashboardglobelocation.cpp rename to modules/globebrowsing/dashboard/dashboarditemglobelocation.cpp index 01ab242a53..fbf9728ae7 100644 --- a/modules/globebrowsing/dashboard/dashboardglobelocation.cpp +++ b/modules/globebrowsing/dashboard/dashboarditemglobelocation.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include @@ -111,7 +111,8 @@ documentation::Documentation DashboardItemGlobeLocation::Documentation() { }; } -DashboardItemGlobeLocation::DashboardItemGlobeLocation(const ghoul::Dictionary& dictionary) +DashboardItemGlobeLocation::DashboardItemGlobeLocation( + const ghoul::Dictionary& dictionary) : DashboardItem(dictionary) , _fontName(FontNameInfo, KeyFontMono) , _fontSize(FontSizeInfo, DefaultFontSize, 6.f, 144.f, 1.f) diff --git a/modules/globebrowsing/dashboard/dashboardglobelocation.h b/modules/globebrowsing/dashboard/dashboarditemglobelocation.h similarity index 97% rename from modules/globebrowsing/dashboard/dashboardglobelocation.h rename to modules/globebrowsing/dashboard/dashboarditemglobelocation.h index e62cc6f207..1f100e3d44 100644 --- a/modules/globebrowsing/dashboard/dashboardglobelocation.h +++ b/modules/globebrowsing/dashboard/dashboarditemglobelocation.h @@ -23,7 +23,7 @@ ****************************************************************************************/ #ifndef __OPENSPACE_MODULE_GLOBEBROWSING___DASHBOARDITEMGLOBELOCATION___H__ -#define __OPENSPACE_MDOULE_GLOBEBROWSING___DASHBOARDITEMGLOBELOCATION___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___DASHBOARDITEMGLOBELOCATION___H__ #include diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index 55e878b52f..d77f6702d9 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include #include diff --git a/modules/imgui/include/imgui_include.h b/modules/imgui/include/imgui_include.h index 0d6552f304..736ab48e3d 100644 --- a/modules/imgui/include/imgui_include.h +++ b/modules/imgui/include/imgui_include.h @@ -35,7 +35,7 @@ #endif // __clang__ #include - +#include #ifdef __clang__ #pragma clang diagnostic pop diff --git a/modules/imgui/src/guiiswacomponent.cpp b/modules/imgui/src/guiiswacomponent.cpp index b0429fba02..4560983675 100644 --- a/modules/imgui/src/guiiswacomponent.cpp +++ b/modules/imgui/src/guiiswacomponent.cpp @@ -27,8 +27,8 @@ #include #include -#include #include +#include #include #include diff --git a/modules/imgui/src/guispacetimecomponent.cpp b/modules/imgui/src/guispacetimecomponent.cpp index b1cd7e52c1..8300e708b2 100644 --- a/modules/imgui/src/guispacetimecomponent.cpp +++ b/modules/imgui/src/guispacetimecomponent.cpp @@ -40,8 +40,6 @@ #include -#include - namespace { const ImVec2 Size = ImVec2(350, 500); diff --git a/modules/imgui/src/renderproperties.cpp b/modules/imgui/src/renderproperties.cpp index 071fa1663a..4e43d06b6e 100644 --- a/modules/imgui/src/renderproperties.cpp +++ b/modules/imgui/src/renderproperties.cpp @@ -48,7 +48,6 @@ #include #include #include -#include namespace openspace { diff --git a/modules/iswa/CMakeLists.txt b/modules/iswa/CMakeLists.txt index f6e9443c92..e35a3ce470 100644 --- a/modules/iswa/CMakeLists.txt +++ b/modules/iswa/CMakeLists.txt @@ -25,7 +25,6 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/ext/json.h ${CMAKE_CURRENT_SOURCE_DIR}/util/iswamanager.h ${CMAKE_CURRENT_SOURCE_DIR}/util/dataprocessor.h ${CMAKE_CURRENT_SOURCE_DIR}/util/dataprocessortext.h diff --git a/modules/iswa/ext/json/json.hpp b/modules/iswa/ext/json/json.hpp deleted file mode 100644 index db73336723..0000000000 --- a/modules/iswa/ext/json/json.hpp +++ /dev/null @@ -1,8236 +0,0 @@ -/*! -@mainpage - -These pages contain the API documentation of JSON for Modern C++, a C++11 -header-only JSON class. - -Class @ref nlohmann::basic_json is a good entry point for the documentation. - -@copyright The code is licensed under the [MIT - License](http://opensource.org/licenses/MIT): -
- Copyright © 2013-2016 Niels Lohmann. -
- Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - 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. - -@author [Niels Lohmann](http://nlohmann.me) -@see https://github.com/nlohmann/json to download the source code - -@version 1.1.0 -*/ - -#ifndef NLOHMANN_JSON_HPP -#define NLOHMANN_JSON_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// enable ssize_t on MinGW -#ifdef __GNUC__ - #ifdef __MINGW32__ - #include - #endif -#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 - -// enable ssize_t for MSVC -#ifdef _MSC_VER - #include - using ssize_t = SSIZE_T; -#endif - -/*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -namespace nlohmann -{ - - -/*! -@brief unnamed namespace with internal helper functions -@since version 1.0.0 -*/ -namespace -{ -/*! -@brief Helper to determine whether there's a key_type for T. -@sa http://stackoverflow.com/a/7728728/266378 -*/ -template -struct has_mapped_type -{ - private: - template static char test(typename C::mapped_type*); - template static char (&test(...))[2]; - public: - static constexpr bool value = sizeof(test(0)) == 1; -}; - -} - -/*! -@brief a class to store JSON values - -@tparam ObjectType type for JSON objects (@c std::map by default; will be used -in @ref object_t) -@tparam ArrayType type for JSON arrays (@c std::vector by default; will be used -in @ref array_t) -@tparam StringType type for JSON strings and object keys (@c std::string by -default; will be used in @ref string_t) -@tparam BooleanType type for JSON booleans (@c `bool` by default; will be used -in @ref boolean_t) -@tparam NumberIntegerType type for JSON integer numbers (@c `int64_t` by -default; will be used in @ref number_integer_t) -@tparam NumberFloatType type for JSON floating-point numbers (@c `double` by -default; will be used in @ref number_float_t) -@tparam AllocatorType type of the allocator to use (@c `std::allocator` by -default) - -@requirement The class satisfies the following concept requirements: -- Basic - - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null value. - - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): - A JSON value can be constructed from an rvalue argument. - - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): - A JSON value can be copy-constructed from an lvalue expression. - - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): - A JSON value van be assigned from an rvalue argument. - - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): - A JSON value can be copy-assigned from an lvalue expression. - - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): - JSON values can be destructed. -- Layout - - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): - JSON values have - [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the class - has no virtual functions or (virtual) base classes. -- Library-wide - - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): - JSON values can be compared with `==`, see @ref - operator==(const_reference,const_reference). - - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): - JSON values can be compared with `<`, see @ref - operator<(const_reference,const_reference). - - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): - Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of - other compatible types, using unqualified function call @ref swap(). - - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): - JSON values can be compared against `std::nullptr_t` objects which are used - to model the `null` value. -- Container - - [Container](http://en.cppreference.com/w/cpp/concept/Container): - JSON values can be used like STL containers and provide iterator access. - - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); - JSON values can be used like STL containers and provide reverse iterator - access. - -@internal -@note ObjectType trick from http://stackoverflow.com/a/9860911 -@endinternal - -@see RFC 7159 - -@since version 1.0.0 - -@nosubgrouping -*/ -template < - template class ObjectType = std::map, - template class ArrayType = std::vector, - class StringType = std::string, - class BooleanType = bool, - class NumberIntegerType = int64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator - > -class basic_json -{ - private: - /// workaround type for MSVC - using basic_json_t = basic_json; - - public: - - ///////////////////// - // container types // - ///////////////////// - - /// @name container types - /// @{ - - /// the type of elements in a basic_json container - using value_type = basic_json; - - /// the type of an element reference - using reference = value_type&; - /// the type of an element const reference - using const_reference = const value_type&; - - /// a type to represent differences between iterators - using difference_type = std::ptrdiff_t; - /// a type to represent container sizes - using size_type = std::size_t; - - /// the allocator type - using allocator_type = AllocatorType; - - /// the type of an element pointer - using pointer = typename std::allocator_traits::pointer; - /// the type of an element const pointer - using const_pointer = typename std::allocator_traits::const_pointer; - - // forward declaration - template class json_reverse_iterator; - - /// an iterator for a basic_json container - class iterator; - /// a const iterator for a basic_json container - class const_iterator; - /// a reverse iterator for a basic_json container - using reverse_iterator = json_reverse_iterator; - /// a const reverse iterator for a basic_json container - using const_reverse_iterator = json_reverse_iterator; - - /// @} - - - /*! - @brief returns the allocator associated with the container - */ - static allocator_type get_allocator() - { - return allocator_type(); - } - - - /////////////////////////// - // JSON value data types // - /////////////////////////// - - /// @name JSON value data types - /// @{ - - /*! - @brief a type for an object - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: - > An object is an unordered collection of zero or more name/value pairs, - > where a name is a string and a value is a string, number, boolean, null, - > object, or array. - - To store objects in C++, a type is defined by the template parameters - described below. - - @tparam ObjectType the container to store objects (e.g., `std::map` or - `std::unordered_map`) - @tparam StringType the type of the keys or names (e.g., `std::string`). The - comparison function `std::less` is used to order elements - inside the container. - @tparam AllocatorType the allocator to use for objects (e.g., - `std::allocator`) - - #### Default type - - With the default values for @a ObjectType (`std::map`), @a StringType - (`std::string`), and @a AllocatorType (`std::allocator`), the default value - for @a object_t is: - - @code {.cpp} - std::map< - std::string, // key_type - basic_json, // value_type - std::less, // key_compare - std::allocator> // allocator_type - > - @endcode - - #### Behavior - - The choice of @a object_t influences the behavior of the JSON class. With - the default type, objects have the following behavior: - - - When all names are unique, objects will be interoperable in the sense - that all software implementations receiving that object will agree on the - name-value mappings. - - When the names within an object are not unique, later stored name/value - pairs overwrite previously stored name/value pairs, leaving the used - names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will - be treated as equal and both stored as `{"key": 1}`. - - Internally, name/value pairs are stored in lexicographical order of the - names. Objects will also be serialized (see @ref dump) in this order. For - instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored and - serialized as `{"a": 2, "b": 1}`. - - When comparing objects, the order of the name/value pairs is irrelevant. - This makes objects interoperable in the sense that they will not be - affected by these differences. For instance, `{"b": 1, "a": 2}` and - `{"a": 2, "b": 1}` will be treated as equal. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. - - In this class, the object's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the @ref - max_size function of a JSON object. - - #### Storage - - Objects are stored as pointers in a @ref basic_json type. That is, for any - access to object values, a pointer of type `object_t*` must be dereferenced. - - @sa @ref array_t -- type for an array value - - @since version 1.0.0 - - @note The order name/value pairs are added to the object is *not* preserved - by the library. Therefore, iterating an object may return name/value pairs - in a different order than they were originally stored. In fact, keys will - be traversed in alphabetical order as `std::map` with `std::less` is used - by default. Please note this behavior conforms to [RFC - 7159](http://rfc7159.net/rfc7159), because any order implements the - specified "unordered" nature of JSON objects. - */ - using object_t = ObjectType, - AllocatorType>>; - - /*! - @brief a type for an array - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: - > An array is an ordered sequence of zero or more values. - - To store objects in C++, a type is defined by the template parameters - explained below. - - @tparam ArrayType container type to store arrays (e.g., `std::vector` or - `std::list`) - @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) - - #### Default type - - With the default values for @a ArrayType (`std::vector`) and @a - AllocatorType (`std::allocator`), the default value for @a array_t is: - - @code {.cpp} - std::vector< - basic_json, // value_type - std::allocator // allocator_type - > - @endcode - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. - - In this class, the array's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the @ref - max_size function of a JSON array. - - #### Storage - - Arrays are stored as pointers in a @ref basic_json type. That is, for any - access to array values, a pointer of type `array_t*` must be dereferenced. - - @sa @ref object_t -- type for an object value - - @since version 1.0.0 - */ - using array_t = ArrayType>; - - /*! - @brief a type for a string - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: - > A string is a sequence of zero or more Unicode characters. - - To store objects in C++, a type is defined by the template parameter - described below. Unicode values are split by the JSON class into byte-sized - characters during deserialization. - - @tparam StringType the container to store strings (e.g., `std::string`). - Note this container is used for keys/names in objects, see @ref object_t. - - #### Default type - - With the default values for @a StringType (`std::string`), the default - value for @a string_t is: - - @code {.cpp} - std::string - @endcode - - #### String comparison - - [RFC 7159](http://rfc7159.net/rfc7159) states: - > Software implementations are typically required to test names of object - > members for equality. Implementations that transform the textual - > representation into sequences of Unicode code units and then perform the - > comparison numerically, code unit by code unit, are interoperable in the - > sense that implementations will agree in all cases on equality or - > inequality of two strings. For example, implementations that compare - > strings with escaped characters unconverted may incorrectly find that - > `"a\\b"` and `"a\u005Cb"` are not equal. - - This implementation is interoperable as it does compare strings code unit - by code unit. - - #### Storage - - String values are stored as pointers in a @ref basic_json type. That is, - for any access to string values, a pointer of type `string_t*` must be - dereferenced. - - @since version 1.0.0 - */ - using string_t = StringType; - - /*! - @brief a type for a boolean - - [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a - type which differentiates the two literals `true` and `false`. - - To store objects in C++, a type is defined by the template parameter @a - BooleanType which chooses the type to use. - - #### Default type - - With the default values for @a BooleanType (`bool`), the default value for - @a boolean_t is: - - @code {.cpp} - bool - @endcode - - #### Storage - - Boolean values are stored directly inside a @ref basic_json type. - - @since version 1.0.0 - */ - using boolean_t = BooleanType; - - /*! - @brief a type for a number (integer) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most programming - > languages. A number is represented in base 10 using decimal digits. It - > contains an integer component that may be prefixed with an optional minus - > sign, which may be followed by a fraction part and/or an exponent part. - > Leading zeros are not allowed. (...) Numeric values that cannot be - > represented in the grammar below (such as Infinity and NaN) are not - > permitted. - - This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. - - To store integer numbers in C++, a type is defined by the template - parameter @a NumberIntegerType which chooses the type to use. - - #### Default type - - With the default values for @a NumberIntegerType (`int64_t`), the default - value for @a number_integer_t is: - - @code {.cpp} - int64_t - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. During - deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. - - When the default type is used, the maximal integer number that can be - stored is `9223372036854775807` (INT64_MAX) and the minimal integer number - that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers - that are out of range will yield over/underflow when used in a constructor. - During deserialization, too large or small integer numbers will be - automatically be stored as @ref number_float_t. - - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. - - As this range is a subrange of the exactly supported range [INT64_MIN, - INT64_MAX], this class's integer type is interoperable. - - #### Storage - - Integer number values are stored directly inside a @ref basic_json type. - - @sa @ref number_float_t -- type for number values (floating-point) - - @since version 1.0.0 - */ - using number_integer_t = NumberIntegerType; - - /*! - @brief a type for a number (floating-point) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most programming - > languages. A number is represented in base 10 using decimal digits. It - > contains an integer component that may be prefixed with an optional minus - > sign, which may be followed by a fraction part and/or an exponent part. - > Leading zeros are not allowed. (...) Numeric values that cannot be - > represented in the grammar below (such as Infinity and NaN) are not - > permitted. - - This description includes both integer and floating-point numbers. However, - C++ allows more precise storage if it is known whether the number is an - integer or a floating-point number. Therefore, two different types, @ref - number_integer_t and @ref number_float_t are used. - - To store floating-point numbers in C++, a type is defined by the template - parameter @a NumberFloatType which chooses the type to use. - - #### Default type - - With the default values for @a NumberFloatType (`double`), the default - value for @a number_float_t is: - - @code {.cpp} - double - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in floating-point literals will be ignored. Internally, the - value will be stored as decimal number. For instance, the C++ - floating-point literal `01.2` will be serialized to `1.2`. During - deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) states: - > This specification allows implementations to set limits on the range and - > precision of numbers accepted. Since software that implements IEEE - > 754-2008 binary64 (double precision) numbers is generally available and - > widely used, good interoperability can be achieved by implementations that - > expect no more precision or range than these provide, in the sense that - > implementations will approximate JSON numbers within the expected - > precision. - - This implementation does exactly follow this approach, as it uses double - precision floating-point numbers. Note values smaller than - `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` - will be stored as NaN internally and be serialized to `null`. - - #### Storage - - Floating-point number values are stored directly inside a @ref basic_json - type. - - @sa @ref number_integer_t -- type for number values (integer) - - @since version 1.0.0 - */ - using number_float_t = NumberFloatType; - - /// @} - - - /////////////////////////// - // 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 is_null(), @ref - is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref - is_number(), and @ref is_discarded() rely on it. - - @since version 1.0.0 - */ - enum class value_t : 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 (integer) - number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function - }; - - - private: - /// helper for exception-safe object creation - template - static T* create(Args&& ... args) - { - AllocatorType alloc; - auto deleter = [&](T * object) - { - alloc.deallocate(object, 1); - }; - std::unique_ptr object(alloc.allocate(1), deleter); - alloc.construct(object.get(), std::forward(args)...); - return object.release(); - } - - //////////////////////// - // JSON value storage // - //////////////////////// - - /*! - @brief a JSON value - - The actual storage for a JSON value of the @ref basic_json class. - - @since version 1.0.0 - */ - union json_value - { - /// object (stored with pointer to save storage) - object_t* object; - /// array (stored with pointer to save storage) - array_t* array; - /// string (stored with pointer to save storage) - string_t* string; - /// boolean - boolean_t boolean; - /// number (integer) - number_integer_t number_integer; - /// number (floating-point) - number_float_t number_float; - - /// default constructor (for null values) - json_value() noexcept = default; - /// constructor for booleans - json_value(boolean_t v) noexcept : boolean(v) {} - /// constructor for numbers (integer) - json_value(number_integer_t v) noexcept : number_integer(v) {} - /// constructor for numbers (floating-point) - json_value(number_float_t v) noexcept : number_float(v) {} - /// constructor for empty values of a given type - json_value(value_t t) - { - switch (t) - { - case value_t::object: - { - object = create(); - break; - } - - case value_t::array: - { - array = create(); - break; - } - - case value_t::string: - { - string = create(""); - break; - } - - case value_t::boolean: - { - boolean = boolean_t(false); - break; - } - - case value_t::number_integer: - { - number_integer = number_integer_t(0); - break; - } - - case value_t::number_float: - { - number_float = number_float_t(0.0); - break; - } - - default: - { - break; - } - } - } - - /// constructor for strings - json_value(const string_t& value) - { - string = create(value); - } - - /// constructor for objects - json_value(const object_t& value) - { - object = create(value); - } - - /// constructor for arrays - json_value(const array_t& value) - { - array = create(value); - } - }; - - - public: - ////////////////////////// - // JSON parser callback // - ////////////////////////// - - /*! - @brief JSON callback events - - This enumeration lists the parser events that can trigger calling a - callback function of type @ref parser_callback_t during parsing. - - @since version 1.0.0 - */ - 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 - }; - - /*! - @brief per-element parser callback type - - With a parser callback function, the result of parsing a JSON text can be - influenced. When passed to @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t), it is called on certain - events (passed as @ref parse_event_t via parameter @a event) with a set - recursion depth @a depth and context JSON value @a parsed. The return value - of the callback function is a boolean indicating whether the element that - emitted the callback shall be kept or not. - - We distinguish six scenarios (determined by the event type) in which the - callback function can be called. The following table describes the values - of the parameters @a depth, @a event, and @a parsed. - - parameter @a event | description | parameter @a depth | parameter @a parsed - ------------------ | ----------- | ------------------ | ------------------- - parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded - parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key - parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object - parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded - parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array - parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value - - Discarding a value (i.e., returning `false`) has different effects - depending on the context in which function was called: - - - Discarded values in structured types are skipped. That is, the parser - will behave as if the discarded value was never read. - - In case a value outside a structured type is skipped, it is replaced with - `null`. This case happens if the top-level element is skipped. - - @param[in] depth the depth of the recursion during parsing - - @param[in] event an event of type parse_event_t indicating the context in - the callback function has been called - - @param[in,out] parsed the current intermediate parse result; note that - writing to this value has no effect for parse_event_t::key events - - @return Whether the JSON value which called the function during parsing - should be kept (`true`) or not (`false`). In the latter case, it is either - skipped completely or replaced by an empty discarded object. - - @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t) for examples - - @since version 1.0.0 - */ - using parser_callback_t = std::function; - - - ////////////////// - // constructors // - ////////////////// - - /// @name constructors and destructors - /// @{ - - /*! - @brief create an empty value with a given type - - Create an empty JSON value with a given type. The value will be default - initialized with an empty value which depends on the type: - - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` - - @param[in] value_type the type of the value to create - - @complexity Constant. - - @throw std::bad_alloc if allocation for object, array, or string value - fails - - @liveexample{The following code shows the constructor for different @ref - value_t values,basic_json__value_t} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - @sa @ref basic_json(boolean_t value) -- create a boolean value - @sa @ref basic_json(const string_t&) -- create a string value - @sa @ref basic_json(const object_t&) -- create a object value - @sa @ref basic_json(const array_t&) -- create a array value - @sa @ref basic_json(const number_float_t) -- create a number - (floating-point) value - @sa @ref basic_json(const number_integer_t) -- create a number (integer) - value - - @since version 1.0.0 - */ - basic_json(const value_t value_type) - : m_type(value_type), m_value(value_type) - {} - - /*! - @brief create a null object (implicitly) - - Create a `null` JSON value. This is the implicit version of the `null` - value constructor as it takes no parameters. - - @complexity Constant. - - @requirement This function satisfies the Container requirements: - - The complexity is constant. - - As postcondition, it holds: `basic_json().empty() == true`. - - @liveexample{The following code shows the constructor for a `null` JSON - value.,basic_json} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - - @since version 1.0.0 - */ - basic_json() noexcept = default; - - /*! - @brief create a null object (explicitly) - - Create a `null` JSON value. This is the explicitly version of the `null` - value constructor as it takes a null pointer as parameter. It allows to - create `null` values by explicitly assigning a @c nullptr to a JSON value. - The passed null pointer itself is not read -- it is only used to choose the - right constructor. - - @complexity Constant. - - @liveexample{The following code shows the constructor with null pointer - parameter.,basic_json__nullptr_t} - - @sa @ref basic_json() -- default constructor (implicitly creating a `null` - value) - - @since version 1.0.0 - */ - basic_json(std::nullptr_t) noexcept - : basic_json(value_t::null) - {} - - /*! - @brief create an object (explicit) - - Create an object JSON value with a given content. - - @param[in] val a value for the object - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for object value fails - - @liveexample{The following code shows the constructor with an @ref object_t - parameter.,basic_json__object_t} - - @sa @ref basic_json(const CompatibleObjectType&) -- create an object value - from a compatible STL container - - @since version 1.0.0 - */ - basic_json(const object_t& val) - : m_type(value_t::object), m_value(val) - {} - - /*! - @brief create an object (implicit) - - Create an object JSON value with a given content. This constructor allows - any type that can be used to construct values of type @ref object_t. - Examples include the types `std::map` and `std::unordered_map`. - - @tparam CompatibleObjectType an object type whose `key_type` and - `value_type` is compatible to @ref object_t - - @param[in] val a value for the object - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for object value fails - - @liveexample{The following code shows the constructor with several - compatible object type parameters.,basic_json__CompatibleObjectType} - - @sa @ref basic_json(const object_t&) -- create an object value - - @since version 1.0.0 - */ - template ::value and - std::is_constructible::value, int>::type - = 0> - basic_json(const CompatibleObjectType& val) - : m_type(value_t::object) - { - using std::begin; - using std::end; - m_value.object = create(begin(val), end(val)); - } - - /*! - @brief create an array (explicit) - - Create an array JSON value with a given content. - - @param[in] val a value for the array - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for array value fails - - @liveexample{The following code shows the constructor with an @ref array_t - parameter.,basic_json__array_t} - - @sa @ref basic_json(const CompatibleArrayType&) -- create an array value - from a compatible STL containers - - @since version 1.0.0 - */ - basic_json(const array_t& val) - : m_type(value_t::array), m_value(val) - {} - - /*! - @brief create an array (implicit) - - Create an array JSON value with a given content. This constructor allows - any type that can be used to construct values of type @ref array_t. - Examples include the types `std::vector`, `std::list`, and `std::set`. - - @tparam CompatibleArrayType an object type whose `value_type` is compatible - to @ref array_t - - @param[in] val a value for the array - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for array value fails - - @liveexample{The following code shows the constructor with several - compatible array type parameters.,basic_json__CompatibleArrayType} - - @sa @ref basic_json(const array_t&) -- create an array value - - @since version 1.0.0 - */ - template ::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> - basic_json(const CompatibleArrayType& val) - : m_type(value_t::array) - { - using std::begin; - using std::end; - m_value.array = create(begin(val), end(val)); - } - - /*! - @brief create a string (explicit) - - Create an string JSON value with a given content. - - @param[in] val a value for the string - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for string value fails - - @liveexample{The following code shows the constructor with an @ref string_t - parameter.,basic_json__string_t} - - @sa @ref basic_json(const typename string_t::value_type*) -- create a - string value from a character pointer - @sa @ref basic_json(const CompatibleStringType&) -- create a string value - from a compatible string container - - @since version 1.0.0 - */ - basic_json(const string_t& val) - : m_type(value_t::string), m_value(val) - {} - - /*! - @brief create a string (explicit) - - Create a string JSON value with a given content. - - @param[in] val a literal value for the string - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for string value fails - - @liveexample{The following code shows the constructor with string literal - parameter.,basic_json__string_t_value_type} - - @sa @ref basic_json(const string_t&) -- create a string value - @sa @ref basic_json(const CompatibleStringType&) -- create a string value - from a compatible string container - - @since version 1.0.0 - */ - basic_json(const typename string_t::value_type* val) - : basic_json(string_t(val)) - {} - - /*! - @brief create a string (implicit) - - Create a string JSON value with a given content. - - @param[in] val a value for the string - - @tparam CompatibleStringType an string type which is compatible to @ref - string_t - - @complexity Linear in the size of the passed @a val. - - @throw std::bad_alloc if allocation for string value fails - - @liveexample{The following code shows the construction of a string value - from a compatible type.,basic_json__CompatibleStringType} - - @sa @ref basic_json(const string_t&) -- create a string value - @sa @ref basic_json(const typename string_t::value_type*) -- create a - string value from a character pointer - - @since version 1.0.0 - */ - template ::value, int>::type - = 0> - basic_json(const CompatibleStringType& val) - : basic_json(string_t(val)) - {} - - /*! - @brief create a boolean (explicit) - - Creates a JSON boolean type from a given value. - - @param[in] val a boolean value to store - - @complexity Constant. - - @liveexample{The example below demonstrates boolean - values.,basic_json__boolean_t} - - @since version 1.0.0 - */ - basic_json(boolean_t val) - : m_type(value_t::boolean), m_value(val) - {} - - /*! - @brief create an integer number (explicit) - - Create an integer number JSON value with a given content. - - @tparam T helper type to compare number_integer_t and int (not visible in) - the interface. - - @param[in] val an integer to create a JSON number from - - @note This constructor would have the same signature as @ref - basic_json(const int value), so we need to switch this one off in case - number_integer_t is the same as int. This is done via the helper type @a T. - - @complexity Constant. - - @liveexample{The example below shows the construction of a JSON integer - number value.,basic_json__number_integer_t} - - @sa @ref basic_json(const int) -- create a number value (integer) - @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number - value (integer) from a compatible number type - - @since version 1.0.0 - */ - template::value) - and std::is_same::value - , int>::type = 0> - basic_json(const number_integer_t val) - : m_type(value_t::number_integer), m_value(val) - {} - - /*! - @brief create an integer number from an enum type (explicit) - - Create an integer number JSON value with a given content. - - @param[in] val an integer to create a JSON number from - - @note This constructor allows to pass enums directly to a constructor. As - C++ has no way of specifying the type of an anonymous enum explicitly, we - can only rely on the fact that such values implicitly convert to int. As - int may already be the same type of number_integer_t, we may need to switch - off the constructor @ref basic_json(const number_integer_t). - - @complexity Constant. - - @liveexample{The example below shows the construction of a JSON integer - number value from an anonymous enum.,basic_json__const_int} - - @sa @ref basic_json(const number_integer_t) -- create a number value - (integer) - @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number - value (integer) from a compatible number type - - @since version 1.0.0 - */ - basic_json(const int val) - : m_type(value_t::number_integer), - m_value(static_cast(val)) - {} - - /*! - @brief create an integer number (implicit) - - Create an integer number JSON value with a given content. This constructor - allows any type that can be used to construct values of type @ref - number_integer_t. Examples may include the types `int`, `int32_t`, or - `short`. - - @tparam CompatibleNumberIntegerType an integer type which is compatible to - @ref number_integer_t. - - @param[in] val an integer to create a JSON number from - - @complexity Constant. - - @liveexample{The example below shows the construction of several JSON - integer number values from compatible - types.,basic_json__CompatibleIntegerNumberType} - - @sa @ref basic_json(const number_integer_t) -- create a number value - (integer) - @sa @ref basic_json(const int) -- create a number value (integer) - - @since version 1.0.0 - */ - template::value and - std::numeric_limits::is_integer, CompatibleNumberIntegerType>::type - = 0> - basic_json(const CompatibleNumberIntegerType val) noexcept - : m_type(value_t::number_integer), - m_value(static_cast(val)) - {} - - /*! - @brief create a floating-point number (explicit) - - Create a floating-point number JSON value with a given content. - - @param[in] val a floating-point value to create a JSON number from - - @note RFC 7159 , section 6 - disallows NaN values: - > Numeric values that cannot be represented in the grammar below (such - > as Infinity and NaN) are not permitted. - In case the parameter @a val is not a number, a JSON null value is - created instead. - - @complexity Constant. - - @liveexample{The following example creates several floating-point - values.,basic_json__number_float_t} - - @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number - value (floating-point) from a compatible number type - - @since version 1.0.0 - */ - basic_json(const number_float_t val) - : m_type(value_t::number_float), m_value(val) - { - // replace infinity and NAN by null - if (not std::isfinite(val)) - { - m_type = value_t::null; - m_value = json_value(); - } - } - - /*! - @brief create an floating-point number (implicit) - - Create an floating-point number JSON value with a given content. This - constructor allows any type that can be used to construct values of type - @ref number_float_t. Examples may include the types `float`. - - @tparam CompatibleNumberFloatType a floating-point type which is compatible - to @ref number_float_t. - - @param[in] val a floating-point to create a JSON number from - - @note RFC 7159 , section 6 - disallows NaN values: - > Numeric values that cannot be represented in the grammar below (such - > as Infinity and NaN) are not permitted. - In case the parameter @a val is not a number, a JSON null value is - created instead. - - @complexity Constant. - - @liveexample{The example below shows the construction of several JSON - floating-point number values from compatible - types.,basic_json__CompatibleNumberFloatType} - - @sa @ref basic_json(const number_float_t) -- create a number value - (floating-point) - - @since version 1.0.0 - */ - template::value and - std::is_floating_point::value>::type - > - basic_json(const CompatibleNumberFloatType val) noexcept - : basic_json(number_float_t(val)) - {} - - /*! - @brief create a container (array or object) from an initializer list - - Creates a JSON value of type array or object from the passed initializer - list @a init. In case @a type_deduction is `true` (default), the type of - the JSON value to be created is deducted from the initializer list @a init - according to the following rules: - - 1. If the list is empty, an empty JSON object value `{}` is created. - 2. If the list consists of pairs whose first element is a string, a JSON - object value is created where the first elements of the pairs are treated - as keys and the second elements are as values. - 3. In all other cases, an array is created. - - The rules aim to create the best fit between a C++ initializer list and - JSON values. The rationale is as follows: - - 1. The empty initializer list is written as `{}` which is exactly an empty - JSON object. - 2. C++ has now way of describing mapped types other than to list a list of - pairs. As JSON requires that keys must be of type string, rule 2 is the - weakest constraint one can pose on initializer lists to interpret them as - an object. - 3. In all other cases, the initializer list could not be interpreted as - JSON object type, so interpreting it as JSON array type is safe. - - With the rules described above, the following JSON values cannot be - expressed by an initializer list: - - - the empty array (`[]`): use @ref array(std::initializer_list) - with an empty initializer list in this case - - arrays whose elements satisfy rule 2: use @ref - array(std::initializer_list) with the same initializer list - in this case - - @note When used without parentheses around an empty initializer list, @ref - basic_json() is called instead of this function, yielding the JSON null - value. - - @param[in] init initializer list with JSON values - - @param[in] type_deduction internal parameter; when set to `true`, the type - of the JSON value is deducted from the initializer list @a init; when set - to `false`, the type provided via @a manual_type is forced. This mode is - used by the functions @ref array(std::initializer_list) and - @ref object(std::initializer_list). - - @param[in] manual_type internal parameter; when @a type_deduction is set to - `false`, the created JSON value will use the provided type (only @ref - value_t::array and @ref value_t::object are valid); when @a type_deduction - is set to `true`, this parameter has no effect - - @throw std::domain_error if @a type_deduction is `false`, @a manual_type is - `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string; example: `"cannot create object from - initializer list"` - - @complexity Linear in the size of the initializer list @a init. - - @liveexample{The example below shows how JSON values are created from - initializer lists,basic_json__list_init_t} - - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list - - @since version 1.0.0 - */ - basic_json(std::initializer_list init, - bool type_deduction = true, - value_t manual_type = value_t::array) - { - // the initializer list could describe an object - bool is_an_object = true; - - // check if each element is an array with two elements whose first - // element is a string - for (const auto& element : init) - { - if (not element.is_array() or element.size() != 2 - or not element[0].is_string()) - { - // we found an element that makes it impossible to use the - // initializer list as object - is_an_object = false; - break; - } - } - - // adjust type if type deduction is not wanted - if (not type_deduction) - { - // if array is wanted, do not create an object though possible - if (manual_type == value_t::array) - { - is_an_object = false; - } - - // if object is wanted but impossible, throw an exception - if (manual_type == value_t::object and not is_an_object) - { - throw std::domain_error("cannot create object from initializer list"); - } - } - - if (is_an_object) - { - // the initializer list is a list of pairs -> create object - m_type = value_t::object; - m_value = value_t::object; - - assert(m_value.object != nullptr); - - for (auto& element : init) - { - m_value.object->emplace(std::move(*(element[0].m_value.string)), std::move(element[1])); - } - } - else - { - // the initializer list describes an array -> create array - m_type = value_t::array; - m_value.array = create(std::move(init)); - } - } - - /*! - @brief explicitly create an array from an initializer list - - Creates a JSON array value from a given initializer list. That is, given a - list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the - initializer list is empty, the empty array `[]` is created. - - @note This function is only needed to express two edge cases that cannot be - realized with the initializer list constructor (@ref - basic_json(std::initializer_list, bool, value_t)). These cases - are: - 1. creating an array whose elements are all pairs whose first element is a - string -- in this case, the initializer list constructor would create an - object, taking the first elements as keys - 2. creating an empty array -- passing the empty initializer list to the - initializer list constructor yields an empty object - - @param[in] init initializer list with JSON values to create an array from - (optional) - - @return JSON array value - - @complexity Linear in the size of @a init. - - @liveexample{The following code shows an example for the @ref array - function.,array} - - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list - - @since version 1.0.0 - */ - static basic_json array(std::initializer_list init = - std::initializer_list()) - { - return basic_json(init, false, value_t::array); - } - - /*! - @brief explicitly create an object from an initializer list - - Creates a JSON object value from a given initializer list. The initializer - lists elements must be pairs, and their first elements must be strings. If - the initializer list is empty, the empty object `{}` is created. - - @note This function is only added for symmetry reasons. In contrast to the - related function @ref array(std::initializer_list), there are - no cases which can only be expressed by this function. That is, any - initializer list @a init can also be passed to the initializer list - constructor - @ref basic_json(std::initializer_list, bool, value_t). - - @param[in] init initializer list to create an object from (optional) - - @return JSON object value - - @throw std::domain_error if @a init is not a pair whose first elements are - strings; thrown by - @ref basic_json(std::initializer_list, bool, value_t) - - @complexity Linear in the size of @a init. - - @liveexample{The following code shows an example for the @ref object - function.,object} - - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list - - @since version 1.0.0 - */ - static basic_json object(std::initializer_list init = - std::initializer_list()) - { - return basic_json(init, false, value_t::object); - } - - /*! - @brief construct an array with count copies of given value - - Constructs a JSON array value by creating @a cnt copies of a passed - value. In case @a cnt is `0`, an empty array is created. As postcondition, - `std::distance(begin(),end()) == cnt` holds. - - @param[in] cnt the number of JSON copies of @a val to create - @param[in] val the JSON value to copy - - @complexity Linear in @a cnt. - - @liveexample{The following code shows examples for the @ref - basic_json(size_type\, const basic_json&) - constructor.,basic_json__size_type_basic_json} - - @since version 1.0.0 - */ - basic_json(size_type cnt, const basic_json& val) - : m_type(value_t::array) - { - m_value.array = create(cnt, val); - } - - /*! - @brief construct a JSON container given an iterator range - - Constructs the JSON value with the contents of the range `[first, last)`. - The semantics depends on the different types a JSON value can have: - - In case of primitive types (number, boolean, or string), @a first must - be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, std::out_of_range is thrown. - - In case of structured types (array, object), the constructor behaves - as similar versions for `std::vector`. - - In case of a null type, std::domain_error is thrown. - - @tparam InputIT an input iterator type (@ref iterator or @ref - const_iterator) - - @param[in] first begin of the range to copy from (included) - @param[in] last end of the range to copy from (excluded) - - @throw std::domain_error if iterators are not compatible; that is, do not - belong to the same JSON value; example: `"iterators are not compatible"` - @throw std::out_of_range if iterators are for a primitive type (number, - boolean, or string) where an out of range error can be detected easily; - example: `"iterators out of range"` - @throw std::bad_alloc if allocation for object, array, or string fails - @throw std::domain_error if called with a null value; example: `"cannot use - construct with iterators from null"` - - @complexity Linear in distance between @a first and @a last. - - @liveexample{The example below shows several ways to create JSON values by - specifying a subrange with iterators.,basic_json__InputIt_InputIt} - - @since version 1.0.0 - */ - template ::value or - std::is_same::value - , int>::type - = 0> - basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type) - { - // make sure iterator fits the current value - if (first.m_object != last.m_object) - { - throw std::domain_error("iterators are not compatible"); - } - - // check if iterator range is complete for primitive values - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) - { - throw std::out_of_range("iterators out of range"); - } - break; - } - - default: - { - break; - } - } - - switch (m_type) - { - case value_t::number_integer: - { - assert(first.m_object != nullptr); - m_value.number_integer = first.m_object->m_value.number_integer; - break; - } - - case value_t::number_float: - { - assert(first.m_object != nullptr); - m_value.number_float = first.m_object->m_value.number_float; - break; - } - - case value_t::boolean: - { - assert(first.m_object != nullptr); - m_value.boolean = first.m_object->m_value.boolean; - break; - } - - case value_t::string: - { - assert(first.m_object != nullptr); - m_value = *first.m_object->m_value.string; - break; - } - - case value_t::object: - { - m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); - break; - } - - case value_t::array: - { - m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); - break; - } - - default: - { - assert(first.m_object != nullptr); - throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name()); - } - } - } - - /////////////////////////////////////// - // other constructors and destructor // - /////////////////////////////////////// - - /*! - @brief copy constructor - - Creates a copy of a given JSON value. - - @param[in] other the JSON value to copy - - @complexity Linear in the size of @a other. - - @requirement This function satisfies the Container requirements: - - The complexity is linear. - - As postcondition, it holds: `other == basic_json(other)`. - - @throw std::bad_alloc if allocation for object, array, or string fails. - - @liveexample{The following code shows an example for the copy - constructor.,basic_json__basic_json} - - @since version 1.0.0 - */ - basic_json(const basic_json& other) - : m_type(other.m_type) - { - switch (m_type) - { - case value_t::object: - { - assert(other.m_value.object != nullptr); - m_value = *other.m_value.object; - break; - } - - case value_t::array: - { - assert(other.m_value.array != nullptr); - m_value = *other.m_value.array; - break; - } - - case value_t::string: - { - assert(other.m_value.string != nullptr); - m_value = *other.m_value.string; - break; - } - - case value_t::boolean: - { - m_value = other.m_value.boolean; - break; - } - - case value_t::number_integer: - { - m_value = other.m_value.number_integer; - break; - } - - case value_t::number_float: - { - m_value = other.m_value.number_float; - break; - } - - default: - { - break; - } - } - } - - /*! - @brief move constructor - - Move constructor. Constructs a JSON value with the contents of the given - value @a other using move semantics. It "steals" the resources from @a - other and leaves it as JSON null value. - - @param[in,out] other value to move to this object - - @post @a other is a JSON null value - - @complexity Constant. - - @liveexample{The code below shows the move constructor explicitly called - via std::move.,basic_json__moveconstructor} - - @since version 1.0.0 - */ - basic_json(basic_json&& other) noexcept - : m_type(std::move(other.m_type)), - m_value(std::move(other.m_value)) - { - // invalidate payload - other.m_type = value_t::null; - other.m_value = {}; - } - - /*! - @brief copy assignment - - Copy assignment operator. Copies a JSON value via the "copy and swap" - strategy: It is expressed in terms of the copy constructor, destructor, and - the swap() member function. - - @param[in] other value to copy from - - @complexity Linear. - - @requirement This function satisfies the Container requirements: - - The complexity is linear. - - @liveexample{The code below shows and example for the copy assignment. It - creates a copy of value `a` which is then swapped with `b`. Finally\, the - copy of `a` (which is the null value after the swap) is - destroyed.,basic_json__copyassignment} - - @since version 1.0.0 - */ - reference& operator=(basic_json other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - using std::swap; - swap(m_type, other.m_type); - swap(m_value, other.m_value); - return *this; - } - - /*! - @brief destructor - - Destroys the JSON value and frees all allocated memory. - - @complexity Linear. - - @requirement This function satisfies the Container requirements: - - The complexity is linear. - - All stored elements are destroyed and all memory is freed. - - @since version 1.0.0 - */ - ~basic_json() - { - switch (m_type) - { - case value_t::object: - { - AllocatorType alloc; - alloc.destroy(m_value.object); - alloc.deallocate(m_value.object, 1); - break; - } - - case value_t::array: - { - AllocatorType alloc; - alloc.destroy(m_value.array); - alloc.deallocate(m_value.array, 1); - break; - } - - case value_t::string: - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - break; - } - - default: - { - // all other types need no specific destructor - break; - } - } - } - - /// @} - - public: - /////////////////////// - // object inspection // - /////////////////////// - - /// @name object inspection - /// @{ - - /*! - @brief serialization - - Serialization function for JSON values. The function tries to mimic - Python's @p json.dumps() function, and currently supports its @p indent - parameter. - - @param[in] indent if indent is nonnegative, then array elements and object - members will be pretty-printed with that indent level. An indent level of 0 - will only insert newlines. -1 (the default) selects the most compact - representation - - @return string containing the serialization of the JSON value - - @complexity Linear. - - @liveexample{The following example shows the effect of different @a indent - parameters to the result of the serialization.,dump} - - @see https://docs.python.org/2/library/json.html#json.dump - - @since version 1.0.0 - */ - string_t dump(const int indent = -1) const - { - std::stringstream ss; - - if (indent >= 0) - { - dump(ss, true, static_cast(indent)); - } - else - { - dump(ss, false, 0); - } - - return ss.str(); - } - - /*! - @brief return the type of the JSON value (explicit) - - Return the type of the JSON value as a value from the @ref value_t - enumeration. - - @return the type of the JSON value - - @complexity Constant. - - @liveexample{The following code exemplifies @ref type() for all JSON - types.,type} - - @since version 1.0.0 - */ - value_t type() const noexcept - { - return m_type; - } - - /*! - @brief return whether type is primitive - - This function returns true iff the JSON type is primitive (string, number, - boolean, or null). - - @return `true` if type is primitive (string, number, boolean, or null), - `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_primitive for all JSON - types.,is_primitive} - - @since version 1.0.0 - */ - bool is_primitive() const noexcept - { - return is_null() or is_string() or is_boolean() or is_number(); - } - - /*! - @brief return whether type is structured - - This function returns true iff the JSON type is structured (array or - object). - - @return `true` if type is structured (array or object), `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_structured for all JSON - types.,is_structured} - - @since version 1.0.0 - */ - bool is_structured() const noexcept - { - return is_array() or is_object(); - } - - /*! - @brief return whether value is null - - This function returns true iff the JSON value is null. - - @return `true` if type is null, `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_null for all JSON - types.,is_null} - - @since version 1.0.0 - */ - bool is_null() const noexcept - { - return m_type == value_t::null; - } - - /*! - @brief return whether value is a boolean - - This function returns true iff the JSON value is a boolean. - - @return `true` if type is boolean, `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_boolean for all JSON - types.,is_boolean} - - @since version 1.0.0 - */ - bool is_boolean() const noexcept - { - return m_type == value_t::boolean; - } - - /*! - @brief return whether value is a number - - This function returns true iff the JSON value is a number. This includes - both integer and floating-point values. - - @return `true` if type is number (regardless whether integer or - floating-type), `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_number for all JSON - types.,is_number} - - @sa @ref is_number_integer() -- check if value is an integer number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 1.0.0 - */ - bool is_number() const noexcept - { - return is_number_integer() or is_number_float(); - } - - /*! - @brief return whether value is an integer number - - This function returns true iff the JSON value is an integer number. This - excludes floating-point values. - - @return `true` if type is an integer number, `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_number_integer for all - JSON types.,is_number_integer} - - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 1.0.0 - */ - bool is_number_integer() const noexcept - { - return m_type == value_t::number_integer; - } - - /*! - @brief return whether value is a floating-point number - - This function returns true iff the JSON value is a floating-point number. - This excludes integer values. - - @return `true` if type is a floating-point number, `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_number_float for all - JSON types.,is_number_float} - - @sa @ref is_number() -- check if value is number - @sa @ref is_number_integer() -- check if value is an integer number - - @since version 1.0.0 - */ - bool is_number_float() const noexcept - { - return m_type == value_t::number_float; - } - - /*! - @brief return whether value is an object - - This function returns true iff the JSON value is an object. - - @return `true` if type is object, `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_object for all JSON - types.,is_object} - - @since version 1.0.0 - */ - bool is_object() const noexcept - { - return m_type == value_t::object; - } - - /*! - @brief return whether value is an array - - This function returns true iff the JSON value is an array. - - @return `true` if type is array, `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_array for all JSON - types.,is_array} - - @since version 1.0.0 - */ - bool is_array() const noexcept - { - return m_type == value_t::array; - } - - /*! - @brief return whether value is a string - - This function returns true iff the JSON value is a string. - - @return `true` if type is string, `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_string for all JSON - types.,is_string} - - @since version 1.0.0 - */ - bool is_string() const noexcept - { - return m_type == value_t::string; - } - - /*! - @brief return whether value is discarded - - This function returns true iff the JSON value was discarded during parsing - with a callback function (see @ref parser_callback_t). - - @note This function will always be `false` for JSON values after parsing. - That is, discarded values can only occur during parsing, but will be - removed when inside a structured value or replaced by null in other cases. - - @return `true` if type is discarded, `false` otherwise. - - @complexity Constant. - - @liveexample{The following code exemplifies @ref is_discarded for all JSON - types.,is_discarded} - - @since version 1.0.0 - */ - bool is_discarded() const noexcept - { - return m_type == value_t::discarded; - } - - /*! - @brief return the type of the JSON value (implicit) - - Implicitly return the type of the JSON value as a value from the @ref - value_t enumeration. - - @return the type of the JSON value - - @complexity Constant. - - @liveexample{The following code exemplifies the value_t operator for all - JSON types.,operator__value_t} - - @since version 1.0.0 - */ - operator value_t() const noexcept - { - return m_type; - } - - /// @} - - private: - ////////////////// - // value access // - ////////////////// - - /// get an object (explicit) - template ::value and - std::is_convertible::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_object()) - { - assert(m_value.object != nullptr); - return T(m_value.object->begin(), m_value.object->end()); - } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } - } - - /// get an object (explicit) - object_t get_impl(object_t*) const - { - if (is_object()) - { - assert(m_value.object != nullptr); - return *(m_value.object); - } - else - { - throw std::domain_error("type must be object, but is " + type_name()); - } - } - - /// get an array (explicit) - template ::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_array()) - { - T to_vector; - assert(m_value.array != nullptr); - std::transform(m_value.array->begin(), m_value.array->end(), - std::inserter(to_vector, to_vector.end()), [](basic_json i) - { - return i.get(); - }); - return to_vector; - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get an array (explicit) - template ::value and - not std::is_same::value - , int>::type = 0> - std::vector get_impl(std::vector*) const - { - if (is_array()) - { - std::vector to_vector; - assert(m_value.array != nullptr); - to_vector.reserve(m_value.array->size()); - std::transform(m_value.array->begin(), m_value.array->end(), - std::inserter(to_vector, to_vector.end()), [](basic_json i) - { - return i.get(); - }); - return to_vector; - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get an array (explicit) - template ::value and - not has_mapped_type::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_array()) - { - assert(m_value.array != nullptr); - return T(m_value.array->begin(), m_value.array->end()); - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get an array (explicit) - array_t get_impl(array_t*) const - { - if (is_array()) - { - assert(m_value.array != nullptr); - return *(m_value.array); - } - else - { - throw std::domain_error("type must be array, but is " + type_name()); - } - } - - /// get a string (explicit) - template ::value - , int>::type = 0> - T get_impl(T*) const - { - if (is_string()) - { - assert(m_value.string != nullptr); - return *m_value.string; - } - else - { - throw std::domain_error("type must be string, but is " + type_name()); - } - } - - /// get a number (explicit) - template::value - , int>::type = 0> - T get_impl(T*) const - { - switch (m_type) - { - case value_t::number_integer: - { - return static_cast(m_value.number_integer); - } - - case value_t::number_float: - { - return static_cast(m_value.number_float); - } - - default: - { - throw std::domain_error("type must be number, but is " + type_name()); - } - } - } - - /// get a boolean (explicit) - boolean_t get_impl(boolean_t*) const - { - if (is_boolean()) - { - return m_value.boolean; - } - else - { - throw std::domain_error("type must be boolean, but is " + type_name()); - } - } - - /// get a pointer to the value (object) - object_t* get_impl_ptr(object_t*) noexcept - { - return is_object() ? m_value.object : nullptr; - } - - /// get a pointer to the value (object) - const object_t* get_impl_ptr(const object_t*) const noexcept - { - return is_object() ? m_value.object : nullptr; - } - - /// get a pointer to the value (array) - array_t* get_impl_ptr(array_t*) noexcept - { - return is_array() ? m_value.array : nullptr; - } - - /// get a pointer to the value (array) - const array_t* get_impl_ptr(const array_t*) const noexcept - { - return is_array() ? m_value.array : nullptr; - } - - /// get a pointer to the value (string) - string_t* get_impl_ptr(string_t*) noexcept - { - return is_string() ? m_value.string : nullptr; - } - - /// get a pointer to the value (string) - const string_t* get_impl_ptr(const string_t*) const noexcept - { - return is_string() ? m_value.string : nullptr; - } - - /// get a pointer to the value (boolean) - boolean_t* get_impl_ptr(boolean_t*) noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } - - /// get a pointer to the value (boolean) - const boolean_t* get_impl_ptr(const boolean_t*) const noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } - - /// get a pointer to the value (integer number) - number_integer_t* get_impl_ptr(number_integer_t*) noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } - - /// get a pointer to the value (integer number) - const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } - - /// get a pointer to the value (floating-point number) - number_float_t* get_impl_ptr(number_float_t*) noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } - - /// get a pointer to the value (floating-point number) - const number_float_t* get_impl_ptr(const number_float_t*) const noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } - - /*! - @brief helper function to implement get_ref() - - This funcion helps to implement get_ref() without code duplication for - const and non-const overloads - - @tparam ThisType will be deduced as `basic_json` or `const basic_json` - - @throw std::domain_error if ReferenceType does not match underlying value - type of the current JSON - */ - template - static ReferenceType get_ref_impl(ThisType& obj) - { - // delegate the call to get_ptr<>() - using PointerType = typename std::add_pointer::type; - auto ptr = obj.template get_ptr(); - - if (ptr != nullptr) - { - return *ptr; - } - else - { - throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + - obj.type_name()); - } - } - - public: - - /// @name value access - /// @{ - - /*! - @brief get a value (explicit) - - Explicit type conversion between the JSON value and a compatible value. - - @tparam ValueType non-pointer type compatible to the JSON value, for - instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays - - @return copy of the JSON value, converted to type @a ValueType - - @throw std::domain_error in case passed type @a ValueType is incompatible - to JSON; example: `"type must be object, but is null"` - - @complexity Linear in the size of the JSON value. - - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get__ValueType_const} - - @internal - The idea of using a casted null pointer to choose the correct - implementation is from . - @endinternal - - @sa @ref operator ValueType() const for implicit conversion - @sa @ref get() for pointer-member access - - @since version 1.0.0 - */ - template::value - , int>::type = 0> - ValueType get() const - { - return get_impl(static_cast(nullptr)); - } - - /*! - @brief get a pointer value (explicit) - - Explicit pointer access to the internally stored JSON value. No copies are - made. - - @warning The pointer becomes invalid if the underlying JSON object changes. - - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref - number_float_t. - - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - - @complexity Constant. - - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get__PointerType} - - @sa @ref get_ptr() for explicit pointer-member access - - @since version 1.0.0 - */ - template::value - , int>::type = 0> - PointerType get() noexcept - { - // delegate the call to get_ptr - return get_ptr(); - } - - /*! - @brief get a pointer value (explicit) - @copydoc get() - */ - template::value - , int>::type = 0> - const PointerType get() const noexcept - { - // delegate the call to get_ptr - return get_ptr(); - } - - /*! - @brief get a pointer value (implicit) - - Implicit pointer access to the internally stored JSON value. No copies are - made. - - @warning Writing data to the pointee of the result yields an undefined - state. - - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref - number_float_t. - - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - - @complexity Constant. - - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} - - @since version 1.0.0 - */ - template::value - , int>::type = 0> - PointerType get_ptr() noexcept - { - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); - } - - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() - */ - template::value - and std::is_const::type>::value - , int>::type = 0> - const PointerType get_ptr() const noexcept - { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); - } - - /*! - @brief get a reference value (implicit) - - Implict reference access to the internally stored JSON value. No copies are - made. - - @warning Writing data to the referee of the result yields an undefined - state. - - @tparam ReferenceType reference type; must be a reference to @ref array_t, - @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or - @ref number_float_t. - - @return reference to the internally stored JSON value if the requested - reference type @a ReferenceType fits to the JSON value; throws - std::domain_error otherwise - - @throw std::domain_error in case passed type @a ReferenceType is - incompatible with the stored JSON value - - @complexity Constant. - - @liveexample{The example shows several calls to `get_ref()`.,get_ref} - - @since version 1.1.0 - */ - template::value - , int>::type = 0> - ReferenceType get_ref() - { - // delegate call to get_ref_impl - return get_ref_impl(*this); - } - - /*! - @brief get a reference value (implicit) - @copydoc get_ref() - */ - template::value - and std::is_const::type>::value - , int>::type = 0> - ReferenceType get_ref() const - { - // delegate call to get_ref_impl - return get_ref_impl(*this); - } - - /*! - @brief get a value (implicit) - - Implicit type conversion between the JSON value and a compatible value. The - call is realized by calling @ref get() const. - - @tparam ValueType non-pointer type compatible to the JSON value, for - instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays. The character type of @ref string_t - as well as an initializer list of this type is excluded to avoid - ambiguities as these types implicitly convert to `std::string`. - - @return copy of the JSON value, converted to type @a ValueType - - @throw std::domain_error in case passed type @a ValueType is incompatible - to JSON, thrown by @ref get() const - - @complexity Linear in the size of the JSON value. - - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,operator__ValueType} - - @since version 1.0.0 - */ - template < typename ValueType, typename - std::enable_if < - not std::is_pointer::value - and not std::is_same::value -#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 - and not std::is_same>::value -#endif - , int >::type = 0 > - operator ValueType() const - { - // delegate the call to get<>() const - return get(); - } - - /// @} - - - //////////////////// - // element access // - //////////////////// - - /// @name element access - /// @{ - - /*! - @brief access specified array element with bounds checking - - Returns a reference to the element at specified location @a idx, with - bounds checking. - - @param[in] idx index of the element to access - - @return reference to the element at index @a idx - - @throw std::domain_error if the JSON value is not an array; example: - `"cannot use at() with string"` - @throw std::out_of_range if the index @a idx is out of range of the array; - that is, `idx >= size()`; example: `"array index 7 is out of range"` - - @complexity Constant. - - @liveexample{The example below shows how array elements can be read and - written using at.,at__size_type} - - @since version 1.0.0 - */ - reference at(size_type idx) - { - // at only works for arrays - if (is_array()) - { - try - { - assert(m_value.array != nullptr); - return m_value.array->at(idx); - } - catch (std::out_of_range&) - { - // create better exception explanation - throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); - } - } - else - { - throw std::domain_error("cannot use at() with " + type_name()); - } - } - - /*! - @brief access specified array element with bounds checking - - Returns a const reference to the element at specified location @a idx, with - bounds checking. - - @param[in] idx index of the element to access - - @return const reference to the element at index @a idx - - @throw std::domain_error if the JSON value is not an array; example: - `"cannot use at() with string"` - @throw std::out_of_range if the index @a idx is out of range of the array; - that is, `idx >= size()`; example: `"array index 7 is out of range"` - - @complexity Constant. - - @liveexample{The example below shows how array elements can be read using - at.,at__size_type_const} - - @since version 1.0.0 - */ - const_reference at(size_type idx) const - { - // at only works for arrays - if (is_array()) - { - try - { - assert(m_value.array != nullptr); - return m_value.array->at(idx); - } - catch (std::out_of_range&) - { - // create better exception explanation - throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); - } - } - else - { - throw std::domain_error("cannot use at() with " + type_name()); - } - } - - /*! - @brief access specified object element with bounds checking - - Returns a reference to the element at with specified key @a key, with - bounds checking. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw std::domain_error if the JSON value is not an object; example: - `"cannot use at() with boolean"` - @throw std::out_of_range if the key @a key is is not stored in the object; - that is, `find(key) == end()`; example: `"key "the fast" not found"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using at.,at__object_t_key_type} - - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - reference at(const typename object_t::key_type& key) - { - // at only works for objects - if (is_object()) - { - try - { - assert(m_value.object != nullptr); - return m_value.object->at(key); - } - catch (std::out_of_range&) - { - // create better exception explanation - throw std::out_of_range("key '" + key + "' not found"); - } - } - else - { - throw std::domain_error("cannot use at() with " + type_name()); - } - } - - /*! - @brief access specified object element with bounds checking - - Returns a const reference to the element at with specified key @a key, with - bounds checking. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw std::domain_error if the JSON value is not an object; example: - `"cannot use at() with boolean"` - @throw std::out_of_range if the key @a key is is not stored in the object; - that is, `find(key) == end()`; example: `"key "the fast" not found"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - at.,at__object_t_key_type_const} - - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - const_reference at(const typename object_t::key_type& key) const - { - // at only works for objects - if (is_object()) - { - try - { - assert(m_value.object != nullptr); - return m_value.object->at(key); - } - catch (std::out_of_range&) - { - // create better exception explanation - throw std::out_of_range("key '" + key + "' not found"); - } - } - else - { - throw std::domain_error("cannot use at() with " + type_name()); - } - } - - /*! - @brief access specified array element - - Returns a reference to the element at specified location @a idx. - - @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), - then the array is silently filled up with `null` values to make `idx` a - valid reference to the last stored element. - - @param[in] idx index of the element to access - - @return reference to the element at index @a idx - - @throw std::domain_error if JSON is not an array or null; example: `"cannot - use operator[] with null"` - - @complexity Constant if @a idx is in the range of the array. Otherwise - linear in `idx - size()`. - - @liveexample{The example below shows how array elements can be read and - written using [] operator. Note the addition of `null` - values.,operatorarray__size_type} - - @since version 1.0.0 - */ - reference operator[](size_type idx) - { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::array; - m_value.array = create(); - } - - // [] only works for arrays - if (is_array()) - { - assert(m_value.array != nullptr); - for (size_t i = m_value.array->size(); i <= idx; ++i) - { - m_value.array->push_back(basic_json()); - } - - return m_value.array->operator[](idx); - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief access specified array element - - Returns a const reference to the element at specified location @a idx. - - @param[in] idx index of the element to access - - @return const reference to the element at index @a idx - - @throw std::domain_error if JSON is not an array; example: `"cannot use - operator[] with null"` - - @complexity Constant. - - @liveexample{The example below shows how array elements can be read using - the [] operator.,operatorarray__size_type_const} - - @since version 1.0.0 - */ - const_reference operator[](size_type idx) const - { - // at only works for arrays - if (is_array()) - { - assert(m_value.array != nullptr); - return m_value.array->operator[](idx); - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the [] operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - reference operator[](const typename object_t::key_type& key) - { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::object; - m_value.object = create(); - } - - // [] only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - return m_value.object->operator[](key); - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the [] operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - const_reference operator[](const typename object_t::key_type& key) const - { - // [] only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the [] operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - template - reference operator[](T * (&key)[n]) - { - return operator[](static_cast(key)); - } - - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @note This function is required for compatibility reasons with Clang. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the [] operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - template - const_reference operator[](T * (&key)[n]) const - { - return operator[](static_cast(key)); - } - - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw std::domain_error if JSON is not an object or null; example: - `"cannot use operator[] with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the [] operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.1.0 - */ - template - reference operator[](T* key) - { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - } - - // at only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - return m_value.object->operator[](key); - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw std::domain_error if JSON is not an object; example: `"cannot use - operator[] with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the [] operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.1.0 - */ - template - const_reference operator[](T* key) const - { - // at only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - else - { - throw std::domain_error("cannot use operator[] with " + type_name()); - } - } - - /*! - @brief access specified object element with default value - - Returns either a copy of an object's element at the specified key @a key or - a given default value if no element with key @a key exists. - - The function is basically equivalent to executing - @code {.cpp} - try { - return at(key); - } catch(std::out_of_range) { - return default_value; - } - @endcode - - @note Unlike @ref at(const typename object_t::key_type&), this function - does not throw if the given key @a key was not found. - - @note Unlike @ref operator[](const typename object_t::key_type& key), this - function does not implicitly add an element to the position defined by @a - key. This function is furthermore also applicable to const objects. - - @param[in] key key of the element to access - @param[in] default_value the value to return if @a key is not found - - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. - - @return copy of the element at key @a key or @a default_value if @a key - is not found - - @throw std::domain_error if JSON is not an object; example: `"cannot use - value() with null"` - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - - @since version 1.0.0 - */ - template ::value - , int>::type = 0> - ValueType value(const typename object_t::key_type& key, ValueType default_value) const - { - // at only works for objects - if (is_object()) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return *it; - } - else - { - return default_value; - } - } - else - { - throw std::domain_error("cannot use value() with " + type_name()); - } - } - - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value() - */ - string_t value(const typename object_t::key_type& key, const char* default_value) const - { - return value(key, string_t(default_value)); - } - - /*! - @brief access the first element - - Returns a reference to the first element in the container. For a JSON - container `c`, the expression `c.front()` is equivalent to `*c.begin()`. - - @return In case of a structured type (array or object), a reference to the - first element is returned. In cast of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @note Calling `front` on an empty container is undefined. - - @throw std::out_of_range when called on null value - - @liveexample{The following code shows an example for @ref front.,front} - - @since version 1.0.0 - */ - reference front() - { - return *begin(); - } - - /*! - @copydoc basic_json::front() - */ - const_reference front() const - { - return *cbegin(); - } - - /*! - @brief access the last element - - Returns a reference to the last element in the container. For a JSON - container `c`, the expression `c.back()` is equivalent to `{ auto tmp = - c.end(); --tmp; return *tmp; }`. - - @return In case of a structured type (array or object), a reference to the - last element is returned. In cast of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @note Calling `back` on an empty container is undefined. - - @throw std::out_of_range when called on null value. - - @liveexample{The following code shows an example for @ref back.,back} - - @since version 1.0.0 - */ - reference back() - { - auto tmp = end(); - --tmp; - return *tmp; - } - - /*! - @copydoc basic_json::back() - */ - const_reference back() const - { - auto tmp = cend(); - --tmp; - return *tmp; - } - - /*! - @brief remove element given an iterator - - Removes the element specified by iterator @a pos. Invalidates iterators and - references at or after the point of the erase, including the end() - iterator. The iterator @a pos must be valid and dereferenceable. Thus the - end() iterator (which is valid, but is not dereferenceable) cannot be used - as a value for @a pos. - - If called on a primitive type other than null, the resulting JSON value - will be `null`. - - @param[in] pos iterator to the element to remove - @return Iterator following the last removed element. If the iterator @a pos - refers to the last element, the end() iterator is returned. - - @tparam InteratorType an @ref iterator or @ref const_iterator - - @throw std::domain_error if called on a `null` value; example: `"cannot use - erase() with null"` - @throw std::domain_error if called on an iterator which does not belong to - the current JSON value; example: `"iterator does not fit current value"` - @throw std::out_of_range if called on a primitive type with invalid - iterator (i.e., any iterator which is not end()); example: `"iterator out - of range"` - - @complexity The complexity depends on the type: - - objects: amortized constant - - arrays: linear in distance between pos and the end of the container - - strings: linear in the length of the string - - other types: constant - - @liveexample{The example shows the result of erase for different JSON - types.,erase__IteratorType} - - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the - given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at the - given index - - @since version 1.0.0 - */ - template ::value or - std::is_same::value - , int>::type - = 0> - InteratorType erase(InteratorType pos) - { - // make sure iterator fits the current value - if (this != pos.m_object) - { - throw std::domain_error("iterator does not fit current value"); - } - - InteratorType result = end(); - - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::string: - { - if (not pos.m_it.primitive_iterator.is_begin()) - { - throw std::out_of_range("iterator out of range"); - } - - if (is_string()) - { - delete m_value.string; - m_value.string = nullptr; - } - - m_type = value_t::null; - break; - } - - case value_t::object: - { - assert(m_value.object != nullptr); - result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); - break; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); - break; - } - - default: - { - throw std::domain_error("cannot use erase() with " + type_name()); - } - } - - return result; - } - - /*! - @brief remove elements given an iterator range - - Removes the element specified by the range `[first; last)`. Invalidates - iterators and references at or after the point of the erase, including the - end() iterator. The iterator @a first does not need to be dereferenceable - if `first == last`: erasing an empty range is a no-op. - - If called on a primitive type other than null, the resulting JSON value - will be `null`. - - @param[in] first iterator to the beginning of the range to remove - @param[in] last iterator past the end of the range to remove - @return Iterator following the last removed element. If the iterator @a - second refers to the last element, the end() iterator is returned. - - @tparam InteratorType an @ref iterator or @ref const_iterator - - @throw std::domain_error if called on a `null` value; example: `"cannot use - erase() with null"` - @throw std::domain_error if called on iterators which does not belong to - the current JSON value; example: `"iterators do not fit current value"` - @throw std::out_of_range if called on a primitive type with invalid - iterators (i.e., if `first != begin()` and `last != end()`); example: - `"iterators out of range"` - - @complexity The complexity depends on the type: - - objects: `log(size()) + std::distance(first, last)` - - arrays: linear in the distance between @a first and @a last, plus linear - in the distance between @a last and end of the container - - strings: linear in the length of the string - - other types: constant - - @liveexample{The example shows the result of erase for different JSON - types.,erase__IteratorType_IteratorType} - - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at the - given index - - @since version 1.0.0 - */ - template ::value or - std::is_same::value - , int>::type - = 0> - InteratorType erase(InteratorType first, InteratorType last) - { - // make sure iterator fits the current value - if (this != first.m_object or this != last.m_object) - { - throw std::domain_error("iterators do not fit current value"); - } - - InteratorType result = end(); - - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) - { - throw std::out_of_range("iterators out of range"); - } - - if (is_string()) - { - delete m_value.string; - m_value.string = nullptr; - } - - m_type = value_t::null; - break; - } - - case value_t::object: - { - assert(m_value.object != nullptr); - result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, - last.m_it.object_iterator); - break; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, - last.m_it.array_iterator); - break; - } - - default: - { - throw std::domain_error("cannot use erase() with " + type_name()); - } - } - - return result; - } - - /*! - @brief remove element from a JSON object given a key - - Removes elements from a JSON object with the key value @a key. - - @param[in] key value of the elements to remove - - @return Number of elements removed. If ObjectType is the default `std::map` - type, the return value will always be `0` (@a key was not found) or `1` (@a - key was found). - - @throw std::domain_error when called on a type other than JSON object; - example: `"cannot use erase() with null"` - - @complexity `log(size()) + count(key)` - - @liveexample{The example shows the effect of erase.,erase__key_type} - - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the - given range - @sa @ref erase(const size_type) -- removes the element from an array at the - given index - - @since version 1.0.0 - */ - size_type erase(const typename object_t::key_type& key) - { - // this erase only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - return m_value.object->erase(key); - } - else - { - throw std::domain_error("cannot use erase() with " + type_name()); - } - } - - /*! - @brief remove element from a JSON array given an index - - Removes element from a JSON array at the index @a idx. - - @param[in] idx index of the element to remove - - @throw std::domain_error when called on a type other than JSON array; - example: `"cannot use erase() with null"` - @throw std::out_of_range when `idx >= size()`; example: `"index out of - range"` - - @complexity Linear in distance between @a idx and the end of the container. - - @liveexample{The example shows the effect of erase.,erase__size_type} - - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the - given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - - @since version 1.0.0 - */ - void erase(const size_type idx) - { - // this erase only works for arrays - if (is_array()) - { - if (idx >= size()) - { - throw std::out_of_range("index out of range"); - } - - assert(m_value.array != nullptr); - m_value.array->erase(m_value.array->begin() + static_cast(idx)); - } - else - { - throw std::domain_error("cannot use erase() with " + type_name()); - } - } - - /*! - @brief find an element in a JSON object - - Finds an element in a JSON object with key equivalent to @a key. If the - element is not found or the JSON value is not an object, end() is returned. - - @param[in] key key value of the element to search for - - @return Iterator to an element with key equivalent to @a key. If no such - element is found, past-the-end (see end()) iterator is returned. - - @complexity Logarithmic in the size of the JSON object. - - @liveexample{The example shows how find is used.,find__key_type} - - @since version 1.0.0 - */ - iterator find(typename object_t::key_type key) - { - auto result = end(); - - if (is_object()) - { - assert(m_value.object != nullptr); - result.m_it.object_iterator = m_value.object->find(key); - } - - return result; - } - - /*! - @brief find an element in a JSON object - @copydoc find(typename object_t::key_type) - */ - const_iterator find(typename object_t::key_type key) const - { - auto result = cend(); - - if (is_object()) - { - assert(m_value.object != nullptr); - result.m_it.object_iterator = m_value.object->find(key); - } - - return result; - } - - /*! - @brief returns the number of occurrences of a key in a JSON object - - Returns the number of elements with key @a key. If ObjectType is the - default `std::map` type, the return value will always be `0` (@a key was - not found) or `1` (@a key was found). - - @param[in] key key value of the element to count - - @return Number of elements with key @a key. If the JSON value is not an - object, the return value will be `0`. - - @complexity Logarithmic in the size of the JSON object. - - @liveexample{The example shows how count is used.,count} - - @since version 1.0.0 - */ - size_type count(typename object_t::key_type key) const - { - // return 0 for all nonobject types - assert(not is_object() or m_value.object != nullptr); - return is_object() ? m_value.object->count(key) : 0; - } - - /// @} - - - /////////////// - // iterators // - /////////////// - - /// @name iterators - /// @{ - - /*! - @brief returns an iterator to the first element - - Returns an iterator to the first element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return iterator to the first element - - @complexity Constant. - - @requirement This function satisfies the Container requirements: - - The complexity is constant. - - @liveexample{The following code shows an example for @ref begin.,begin} - - @since version 1.0.0 - */ - iterator begin() - { - iterator result(this); - result.set_begin(); - return result; - } - - /*! - @copydoc basic_json::cbegin() - */ - const_iterator begin() const - { - return cbegin(); - } - - /*! - @brief returns a const iterator to the first element - - Returns a const iterator to the first element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return const iterator to the first element - - @complexity Constant. - - @requirement This function satisfies the Container requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).begin()`. - - @liveexample{The following code shows an example for @ref cbegin.,cbegin} - - @since version 1.0.0 - */ - const_iterator cbegin() const - { - const_iterator result(this); - result.set_begin(); - return result; - } - - /*! - @brief returns an iterator to one past the last element - - Returns an iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return iterator one past the last element - - @complexity Constant. - - @requirement This function satisfies the Container requirements: - - The complexity is constant. - - @liveexample{The following code shows an example for @ref end.,end} - - @since version 1.0.0 - */ - iterator end() - { - iterator result(this); - result.set_end(); - return result; - } - - /*! - @copydoc basic_json::cend() - */ - const_iterator end() const - { - return cend(); - } - - /*! - @brief returns a const iterator to one past the last element - - Returns a const iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return const iterator one past the last element - - @complexity Constant. - - @requirement This function satisfies the Container requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).end()`. - - @liveexample{The following code shows an example for @ref cend.,cend} - - @since version 1.0.0 - */ - const_iterator cend() const - { - const_iterator result(this); - result.set_end(); - return result; - } - - /*! - @brief returns an iterator to the reverse-beginning - - Returns an iterator to the reverse-beginning; that is, the last element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function satisfies the ReversibleContainer requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(end())`. - - @liveexample{The following code shows an example for @ref rbegin.,rbegin} - - @since version 1.0.0 - */ - reverse_iterator rbegin() - { - return reverse_iterator(end()); - } - - /*! - @copydoc basic_json::crbegin() - */ - const_reverse_iterator rbegin() const - { - return crbegin(); - } - - /*! - @brief returns an iterator to the reverse-end - - Returns an iterator to the reverse-end; that is, one before the first - element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function satisfies the ReversibleContainer requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(begin())`. - - @liveexample{The following code shows an example for @ref rend.,rend} - - @since version 1.0.0 - */ - reverse_iterator rend() - { - return reverse_iterator(begin()); - } - - /*! - @copydoc basic_json::crend() - */ - const_reverse_iterator rend() const - { - return crend(); - } - - /*! - @brief returns a const reverse iterator to the last element - - Returns a const iterator to the reverse-beginning; that is, the last - element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function satisfies the ReversibleContainer requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rbegin()`. - - @liveexample{The following code shows an example for @ref crbegin.,crbegin} - - @since version 1.0.0 - */ - const_reverse_iterator crbegin() const - { - return const_reverse_iterator(cend()); - } - - /*! - @brief returns a const reverse iterator to one before the first - - Returns a const reverse iterator to the reverse-end; that is, one before - the first element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function satisfies the ReversibleContainer requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rend()`. - - @liveexample{The following code shows an example for @ref crend.,crend} - - @since version 1.0.0 - */ - const_reverse_iterator crend() const - { - return const_reverse_iterator(cbegin()); - } - - private: - // forward declaration - template class iteration_proxy; - - public: - /*! - @brief wrapper to access iterator member functions in range-based for - - This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a reference - to the JSON values is returned, so there is no access to the underlying - iterator. - - @note The name of this function is not yet final and may change in the - future. - */ - static iteration_proxy iterator_wrapper(reference cont) - { - return iteration_proxy(cont); - } - - /*! - @copydoc iterator_wrapper(reference) - */ - static iteration_proxy iterator_wrapper(const_reference cont) - { - return iteration_proxy(cont); - } - - /// @} - - - ////////////// - // capacity // - ////////////// - - /// @name capacity - /// @{ - - /*! - @brief checks whether the container is empty - - Checks if a JSON value has no elements. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | @c true - boolean | @c false - string | @c false - number | @c false - object | result of function object_t::empty() - array | result of function array_t::empty() - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy the - Container concept; that is, their empty() functions have constant - complexity. - - @requirement This function satisfies the Container requirements: - - The complexity is constant. - - Has the semantics of `begin() == end()`. - - @liveexample{The following code uses @ref empty to check if a @ref json - object contains any elements.,empty} - - @since version 1.0.0 - */ - bool empty() const noexcept - { - switch (m_type) - { - case value_t::null: - { - // null values are empty - return true; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - return m_value.array->empty(); - } - - case value_t::object: - { - assert(m_value.object != nullptr); - return m_value.object->empty(); - } - - default: - { - // all other types are nonempty - return false; - } - } - } - - /*! - @brief returns the number of elements - - Returns the number of elements in a JSON value. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | @c 0 - boolean | @c 1 - string | @c 1 - number | @c 1 - object | result of function object_t::size() - array | result of function array_t::size() - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy the - Container concept; that is, their size() functions have constant complexity. - - @requirement This function satisfies the Container requirements: - - The complexity is constant. - - Has the semantics of `std::distance(begin(), end())`. - - @liveexample{The following code calls @ref size on the different value - types.,size} - - @since version 1.0.0 - */ - size_type size() const noexcept - { - switch (m_type) - { - case value_t::null: - { - // null values are empty - return 0; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - return m_value.array->size(); - } - - case value_t::object: - { - assert(m_value.object != nullptr); - return m_value.object->size(); - } - - default: - { - // all other types have size 1 - return 1; - } - } - } - - /*! - @brief returns the maximum possible number of elements - - Returns the maximum number of elements a JSON value is able to hold due to - system or library implementation limitations, i.e. `std::distance(begin(), - end())` for the JSON value. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | @c 0 (same as size()) - boolean | @c 1 (same as size()) - string | @c 1 (same as size()) - number | @c 1 (same as size()) - object | result of function object_t::max_size() - array | result of function array_t::max_size() - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy the - Container concept; that is, their max_size() functions have constant - complexity. - - @requirement This function satisfies the Container requirements: - - The complexity is constant. - - Has the semantics of returning `b.size()` where `b` is the largest - possible JSON value. - - @liveexample{The following code calls @ref max_size on the different value - types. Note the output is implementation specific.,max_size} - - @since version 1.0.0 - */ - size_type max_size() const noexcept - { - switch (m_type) - { - case value_t::array: - { - assert(m_value.array != nullptr); - return m_value.array->max_size(); - } - - case value_t::object: - { - assert(m_value.object != nullptr); - return m_value.object->max_size(); - } - - default: - { - // all other types have max_size() == size() - return size(); - } - } - } - - /// @} - - - /////////////// - // modifiers // - /////////////// - - /// @name modifiers - /// @{ - - /*! - @brief clears the contents - - Clears the content of a JSON value and resets it to the default value as - if @ref basic_json(value_t) would have been called: - - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` - - @note Floating-point numbers are set to `0.0` which will be serialized to - `0`. The vale type remains @ref number_float_t. - - @complexity Linear in the size of the JSON value. - - @liveexample{The example below shows the effect of @ref clear to different - JSON types.,clear} - - @since version 1.0.0 - */ - void clear() noexcept - { - switch (m_type) - { - case value_t::number_integer: - { - m_value.number_integer = 0; - break; - } - - case value_t::number_float: - { - m_value.number_float = 0.0; - break; - } - - case value_t::boolean: - { - m_value.boolean = false; - break; - } - - case value_t::string: - { - assert(m_value.string != nullptr); - m_value.string->clear(); - break; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - m_value.array->clear(); - break; - } - - case value_t::object: - { - assert(m_value.object != nullptr); - m_value.object->clear(); - break; - } - - default: - { - break; - } - } - } - - /*! - @brief add an object to an array - - Appends the given element @a val to the end of the JSON value. If the - function is called on a JSON null value, an empty array is created before - appending @a val. - - @param val the value to add to the JSON array - - @throw std::domain_error when called on a type other than JSON array or - null; example: `"cannot use push_back() with number"` - - @complexity Amortized constant. - - @liveexample{The example shows how `push_back` and `+=` can be used to add - elements to a JSON array. Note how the `null` value was silently converted - to a JSON array.,push_back} - - @since version 1.0.0 - */ - void push_back(basic_json&& val) - { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - throw std::domain_error("cannot use push_back() with " + type_name()); - } - - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - } - - // add element to array (move semantics) - assert(m_value.array != nullptr); - m_value.array->push_back(std::move(val)); - // invalidate object - val.m_type = value_t::null; - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - reference operator+=(basic_json&& val) - { - push_back(std::move(val)); - return *this; - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - void push_back(const basic_json& val) - { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - throw std::domain_error("cannot use push_back() with " + type_name()); - } - - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - } - - // add element to array - assert(m_value.array != nullptr); - m_value.array->push_back(val); - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - reference operator+=(const basic_json& val) - { - push_back(val); - return *this; - } - - /*! - @brief add an object to an object - - Inserts the given element @a val to the JSON object. If the function is - called on a JSON null value, an empty object is created before inserting @a - val. - - @param[in] val the value to add to the JSON object - - @throw std::domain_error when called on a type other than JSON object or - null; example: `"cannot use push_back() with number"` - - @complexity Logarithmic in the size of the container, O(log(`size()`)). - - @liveexample{The example shows how `push_back` and `+=` can be used to add - elements to a JSON object. Note how the `null` value was silently converted - to a JSON object.,push_back__object_t__value} - - @since version 1.0.0 - */ - void push_back(const typename object_t::value_type& val) - { - // push_back only works for null objects or objects - if (not(is_null() or is_object())) - { - throw std::domain_error("cannot use push_back() with " + type_name()); - } - - // transform null object into an object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - } - - // add element to array - assert(m_value.object != nullptr); - m_value.object->insert(val); - } - - /*! - @brief add an object to an object - @copydoc push_back(const typename object_t::value_type&) - */ - reference operator+=(const typename object_t::value_type& val) - { - push_back(val); - return operator[](val.first); - } - - /*! - @brief inserts element - - Inserts element @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] val element to insert - @return iterator pointing to the inserted @a val. - - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - - @complexity Constant plus linear in the distance between pos and end of the - container. - - @liveexample{The example shows how insert is used.,insert} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, const basic_json& val) - { - // insert only works for arrays - if (is_array()) - { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - throw std::domain_error("iterator does not fit current value"); - } - - // insert to array and return iterator - iterator result(this); - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); - return result; - } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } - } - - /*! - @brief inserts element - @copydoc insert(const_iterator, const basic_json&) - */ - iterator insert(const_iterator pos, basic_json&& val) - { - return insert(pos, val); - } - - /*! - @brief inserts elements - - Inserts @a cnt copies of @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] cnt number of copies of @a val to insert - @param[in] val element to insert - @return iterator pointing to the first element inserted, or @a pos if - `cnt==0` - - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - - @complexity Linear in @a cnt plus linear in the distance between @a pos - and end of the container. - - @liveexample{The example shows how insert is used.,insert__count} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, size_type cnt, const basic_json& val) - { - // insert only works for arrays - if (is_array()) - { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - throw std::domain_error("iterator does not fit current value"); - } - - // insert to array and return iterator - iterator result(this); - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); - return result; - } - else - { - throw std::domain_error("cannot use insert() with " + type_name()); - } - } - - /*! - @brief inserts elements - - Inserts elements from range `[first, last)` before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert - - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - @throw std::domain_error if @a first and @a last do not belong to the same - JSON value; example: `"iterators do not fit"` - @throw std::domain_error if @a first or @a last are iterators into - container for which insert is called; example: `"passed iterators may not - belong to container"` - - @return iterator pointing to the first element inserted, or @a pos if - `first==last` - - @complexity Linear in `std::distance(first, last)` plus linear in the - distance between @a pos and end of the container. - - @liveexample{The example shows how insert is used.,insert__range} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, const_iterator first, const_iterator last) - { - // insert only works for arrays - if (not is_array()) - { - throw std::domain_error("cannot use insert() with " + type_name()); - } - - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - throw std::domain_error("iterator does not fit current value"); - } - - if (first.m_object != last.m_object) - { - throw std::domain_error("iterators do not fit"); - } - - if (first.m_object == this or last.m_object == this) - { - throw std::domain_error("passed iterators may not belong to container"); - } - - // insert to array and return iterator - iterator result(this); - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->insert( - pos.m_it.array_iterator, - first.m_it.array_iterator, - last.m_it.array_iterator); - return result; - } - - /*! - @brief inserts elements - - Inserts elements from initializer list @a ilist before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] ilist initializer list to insert the values from - - @throw std::domain_error if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw std::domain_error if @a pos is not an iterator of *this; example: - `"iterator does not fit current value"` - - @return iterator pointing to the first element inserted, or @a pos if - `ilist` is empty - - @complexity Linear in `ilist.size()` plus linear in the distance between @a - pos and end of the container. - - @liveexample{The example shows how insert is used.,insert__ilist} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, std::initializer_list ilist) - { - // insert only works for arrays - if (not is_array()) - { - throw std::domain_error("cannot use insert() with " + type_name()); - } - - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - throw std::domain_error("iterator does not fit current value"); - } - - // insert to array and return iterator - iterator result(this); - assert(m_value.array != nullptr); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); - return result; - } - - /*! - @brief exchanges the values - - Exchanges the contents of the JSON value with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other JSON value to exchange the contents with - - @complexity Constant. - - @liveexample{The example below shows how JSON arrays can be - swapped.,swap__reference} - - @since version 1.0.0 - */ - void swap(reference other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - std::swap(m_type, other.m_type); - std::swap(m_value, other.m_value); - } - - /*! - @brief exchanges the values - - Exchanges the contents of a JSON array with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other array to exchange the contents with - - @throw std::domain_error when JSON value is not an array; example: `"cannot - use swap() with string"` - - @complexity Constant. - - @liveexample{The example below shows how JSON values can be - swapped.,swap__array_t} - - @since version 1.0.0 - */ - void swap(array_t& other) - { - // swap only works for arrays - if (is_array()) - { - assert(m_value.array != nullptr); - std::swap(*(m_value.array), other); - } - else - { - throw std::domain_error("cannot use swap() with " + type_name()); - } - } - - /*! - @brief exchanges the values - - Exchanges the contents of a JSON object with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other object to exchange the contents with - - @throw std::domain_error when JSON value is not an object; example: - `"cannot use swap() with string"` - - @complexity Constant. - - @liveexample{The example below shows how JSON values can be - swapped.,swap__object_t} - - @since version 1.0.0 - */ - void swap(object_t& other) - { - // swap only works for objects - if (is_object()) - { - assert(m_value.object != nullptr); - std::swap(*(m_value.object), other); - } - else - { - throw std::domain_error("cannot use swap() with " + type_name()); - } - } - - /*! - @brief exchanges the values - - Exchanges the contents of a JSON string with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other string to exchange the contents with - - @throw std::domain_error when JSON value is not a string; example: `"cannot - use swap() with boolean"` - - @complexity Constant. - - @liveexample{The example below shows how JSON values can be - swapped.,swap__string_t} - - @since version 1.0.0 - */ - void swap(string_t& other) - { - // swap only works for strings - if (is_string()) - { - assert(m_value.string != nullptr); - std::swap(*(m_value.string), other); - } - else - { - throw std::domain_error("cannot use swap() with " + type_name()); - } - } - - /// @} - - - ////////////////////////////////////////// - // lexicographical comparison operators // - ////////////////////////////////////////// - - /// @name lexicographical comparison operators - /// @{ - - private: - /*! - @brief comparison operator for JSON types - - Returns an ordering that is similar to Python: - - order: null < boolean < number < object < array < string - - furthermore, each type is not smaller than itself - - @since version 1.0.0 - */ - friend bool operator<(const value_t lhs, const value_t rhs) - { - static constexpr std::array order = {{ - 0, // null - 3, // object - 4, // array - 5, // string - 1, // boolean - 2, // integer - 2 // float - } - }; - - // discarded values are not comparable - if (lhs == value_t::discarded or rhs == value_t::discarded) - { - return false; - } - - return order[static_cast(lhs)] < order[static_cast(rhs)]; - } - - public: - /*! - @brief comparison: equal - - Compares two JSON values for equality according to the following rules: - - Two JSON values are equal if (1) they are from the same type and (2) - their stored values are the same. - - Integer and floating-point numbers are automatically converted before - comparison. Floating-point numbers are compared indirectly: two - floating-point numbers `f1` and `f2` are considered equal if neither - `f1 > f2` nor `f2 > f1` holds. - - Two JSON null values are equal. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are equal - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__equal} - - @since version 1.0.0 - */ - friend bool operator==(const_reference lhs, const_reference rhs) noexcept - { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); - - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); - return *lhs.m_value.array == *rhs.m_value.array; - } - case value_t::object: - { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); - return *lhs.m_value.object == *rhs.m_value.object; - } - case value_t::null: - { - return true; - } - case value_t::string: - { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); - return *lhs.m_value.string == *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean == rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer == rhs.m_value.number_integer; - } - case value_t::number_float: - { - return lhs.m_value.number_float == rhs.m_value.number_float; - } - default: - { - return false; - } - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); - } - return false; - } - - /*! - @brief comparison: equal - - The functions compares the given JSON value against a null pointer. As the - null pointer can be used to initialize a JSON value to null, a comparison - of JSON value @a v with a null pointer should be equivalent to call - `v.is_null()`. - - @param[in] v JSON value to consider - @return whether @a v is null - - @complexity Constant. - - @liveexample{The example compares several JSON types to the null pointer. - ,operator__equal__nullptr_t} - - @since version 1.0.0 - */ - friend bool operator==(const_reference v, std::nullptr_t) noexcept - { - return v.is_null(); - } - - /*! - @brief comparison: equal - @copydoc operator==(const_reference, std::nullptr_t) - */ - friend bool operator==(std::nullptr_t, const_reference v) noexcept - { - return v.is_null(); - } - - /*! - @brief comparison: not equal - - Compares two JSON values for inequality by calculating `not (lhs == rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are not equal - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__notequal} - - @since version 1.0.0 - */ - friend bool operator!=(const_reference lhs, const_reference rhs) noexcept - { - return not (lhs == rhs); - } - - /*! - @brief comparison: not equal - - The functions compares the given JSON value against a null pointer. As the - null pointer can be used to initialize a JSON value to null, a comparison - of JSON value @a v with a null pointer should be equivalent to call - `not v.is_null()`. - - @param[in] v JSON value to consider - @return whether @a v is not null - - @complexity Constant. - - @liveexample{The example compares several JSON types to the null pointer. - ,operator__notequal__nullptr_t} - - @since version 1.0.0 - */ - friend bool operator!=(const_reference v, std::nullptr_t) noexcept - { - return not v.is_null(); - } - - /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, std::nullptr_t) - */ - friend bool operator!=(std::nullptr_t, const_reference v) noexcept - { - return not v.is_null(); - } - - /*! - @brief comparison: less than - - Compares whether one JSON value @a lhs is less than another JSON value @a - rhs according to the following rules: - - If @a lhs and @a rhs have the same type, the values are compared using - the default `<` operator. - - Integer and floating-point numbers are automatically converted before - comparison - - In case @a lhs and @a rhs have different types, the values are ignored - and the order of the types is considered, see - @ref operator<(const value_t, const value_t). - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than @a rhs - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__less} - - @since version 1.0.0 - */ - friend bool operator<(const_reference lhs, const_reference rhs) noexcept - { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); - - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); - return *lhs.m_value.array < *rhs.m_value.array; - } - case value_t::object: - { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); - return *lhs.m_value.object < *rhs.m_value.object; - } - case value_t::null: - { - return false; - } - case value_t::string: - { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); - return *lhs.m_value.string < *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean < rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer < rhs.m_value.number_integer; - } - case value_t::number_float: - { - return lhs.m_value.number_float < rhs.m_value.number_float; - } - default: - { - return false; - } - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) < - rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float < - static_cast(rhs.m_value.number_integer); - } - - // We only reach this line if we cannot compare values. In that case, - // we compare types. Note we have to call the operator explicitly, - // because MSVC has problems otherwise. - return operator<(lhs_type, rhs_type); - } - - /*! - @brief comparison: less than or equal - - Compares whether one JSON value @a lhs is less than or equal to another - JSON value by calculating `not (rhs < lhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than or equal to @a rhs - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__greater} - - @since version 1.0.0 - */ - friend bool operator<=(const_reference lhs, const_reference rhs) noexcept - { - return not (rhs < lhs); - } - - /*! - @brief comparison: greater than - - Compares whether one JSON value @a lhs is greater than another - JSON value by calculating `not (lhs <= rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than to @a rhs - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__lessequal} - - @since version 1.0.0 - */ - friend bool operator>(const_reference lhs, const_reference rhs) noexcept - { - return not (lhs <= rhs); - } - - /*! - @brief comparison: greater than or equal - - Compares whether one JSON value @a lhs is greater than or equal to another - JSON value by calculating `not (lhs < rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than or equal to @a rhs - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__greaterequal} - - @since version 1.0.0 - */ - friend bool operator>=(const_reference lhs, const_reference rhs) noexcept - { - return not (lhs < rhs); - } - - /// @} - - - /////////////////// - // serialization // - /////////////////// - - /// @name serialization - /// @{ - - /*! - @brief serialize to stream - - Serialize the given JSON value @a j to the output stream @a o. The JSON - value will be serialized using the @ref dump member function. The - indentation of the output can be controlled with the member variable - `width` of the output stream @a o. For instance, using the manipulator - `std::setw(4)` on @a o sets the indentation level to `4` and the - serialization result is the same as calling `dump(4)`. - - @param[in,out] o stream to serialize to - @param[in] j JSON value to serialize - - @return the stream @a o - - @complexity Linear. - - @liveexample{The example below shows the serialization with different - parameters to `width` to adjust the indentation level.,operator_serialize} - - @since version 1.0.0 - */ - friend std::ostream& operator<<(std::ostream& o, const basic_json& j) - { - // read width member and use it as indentation parameter if nonzero - const bool pretty_print = (o.width() > 0); - const auto indentation = (pretty_print ? o.width() : 0); - - // reset width to 0 for subsequent calls to this stream - o.width(0); - - // do the actual serialization - j.dump(o, pretty_print, static_cast(indentation)); - return o; - } - - /*! - @brief serialize to stream - @copydoc operator<<(std::ostream&, const basic_json&) - */ - friend std::ostream& operator>>(const basic_json& j, std::ostream& o) - { - return o << j; - } - - /// @} - - - ///////////////////// - // deserialization // - ///////////////////// - - /// @name deserialization - /// @{ - - /*! - @brief deserialize from string - - @param[in] s string to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the parse function with and - without callback function.,parse__string__parser_callback_t} - - @sa @ref parse(std::istream&, parser_callback_t) for a version that reads - from an input stream - - @since version 1.0.0 - */ - static basic_json parse(const string_t& s, parser_callback_t cb = nullptr) - { - return parser(s, cb).parse(); - } - - /*! - @brief deserialize from stream - - @param[in,out] i stream to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the parse function with and - without callback function.,parse__istream__parser_callback_t} - - @sa @ref parse(const string_t&, parser_callback_t) for a version that reads - from a string - - @since version 1.0.0 - */ - static basic_json parse(std::istream& i, parser_callback_t cb = nullptr) - { - return parser(i, cb).parse(); - } - - /*! - @copydoc parse(std::istream&, parser_callback_t) - */ - static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr) - { - return parser(i, cb).parse(); - } - - /*! - @brief deserialize from stream - - Deserializes an input stream to a JSON value. - - @param[in,out] i input stream to read a serialized JSON value from - @param[in,out] j JSON value to write the deserialized input to - - @throw std::invalid_argument in case of parse errors - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below shows how a JSON value is constructed by - reading a serialization from a stream.,operator_deserialize} - - @sa parse(std::istream&, parser_callback_t) for a variant with a parser - callback function to filter values while parsing - - @since version 1.0.0 - */ - friend std::istream& operator<<(basic_json& j, std::istream& i) - { - j = parser(i).parse(); - return i; - } - - /*! - @brief deserialize from stream - @copydoc operator<<(basic_json&, std::istream&) - */ - friend std::istream& operator>>(std::istream& i, basic_json& j) - { - j = parser(i).parse(); - return i; - } - - /// @} - - - private: - /////////////////////////// - // convenience functions // - /////////////////////////// - - /// return the type as string - string_t type_name() const - { - switch (m_type) - { - case value_t::null: - return "null"; - case value_t::object: - return "object"; - case value_t::array: - return "array"; - case value_t::string: - return "string"; - case value_t::boolean: - return "boolean"; - case value_t::discarded: - return "discarded"; - default: - return "number"; - } - } - - /*! - @brief calculates the extra space to escape a JSON string - - @param[in] s the string to escape - @return the number of characters required to escape string @a s - - @complexity Linear in the length of string @a s. - */ - static std::size_t extra_space(const string_t& s) noexcept - { - std::size_t result = 0; - - for (const auto& c : s) - { - switch (c) - { - case '"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - { - // from c (1 byte) to \x (2 bytes) - result += 1; - break; - } - - default: - { - if (c >= 0x00 and c <= 0x1f) - { - // from c (1 byte) to \uxxxx (6 bytes) - result += 5; - } - break; - } - } - } - - return result; - } - - /*! - @brief escape a 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. - - @param[in] s the string to escape - @return the escaped string - - @complexity Linear in the length of string @a s. - */ - static string_t escape_string(const string_t& s) noexcept - { - const auto space = extra_space(s); - if (space == 0) - { - return s; - } - - // create a result string of necessary size - string_t result(s.size() + space, '\\'); - std::size_t pos = 0; - - for (const auto& c : s) - { - switch (c) - { - // quotation mark (0x22) - case '"': - { - result[pos + 1] = '"'; - pos += 2; - break; - } - - // reverse solidus (0x5c) - case '\\': - { - // nothing to change - pos += 2; - break; - } - - // backspace (0x08) - case '\b': - { - result[pos + 1] = 'b'; - pos += 2; - break; - } - - // formfeed (0x0c) - case '\f': - { - result[pos + 1] = 'f'; - pos += 2; - break; - } - - // newline (0x0a) - case '\n': - { - result[pos + 1] = 'n'; - pos += 2; - break; - } - - // carriage return (0x0d) - case '\r': - { - result[pos + 1] = 'r'; - pos += 2; - break; - } - - // horizontal tab (0x09) - case '\t': - { - result[pos + 1] = 't'; - pos += 2; - break; - } - - default: - { - if (c >= 0x00 and c <= 0x1f) - { - // convert a number 0..15 to its hex representation (0..f) - auto hexify = [](const char v) -> char - { - return (v < 10) ? ('0' + v) : ('a' + v - 10); - }; - - // print character c as \uxxxx - for (const char m : - { 'u', '0', '0', hexify(c >> 4), hexify(c & 0x0f) - }) - { - result[++pos] = m; - } - - ++pos; - } - else - { - // all other characters are added as-is - result[pos++] = c; - } - break; - } - } - } - - return result; - } - - /*! - @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. Note that - - - 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 - - @param[out] o stream to write to - @param[in] pretty_print whether the output shall be pretty-printed - @param[in] indent_step the indent level - @param[in] current_indent the current indent level (only used internally) - */ - void dump(std::ostream& o, - const bool pretty_print, - const unsigned int indent_step, - const unsigned int current_indent = 0) const - { - // variable to hold indentation for recursive calls - unsigned int new_indent = current_indent; - - switch (m_type) - { - case value_t::object: - { - assert(m_value.object != nullptr); - - if (m_value.object->empty()) - { - o << "{}"; - return; - } - - o << "{"; - - // increase indentation - if (pretty_print) - { - new_indent += indent_step; - o << "\n"; - } - - for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i) - { - if (i != m_value.object->cbegin()) - { - o << (pretty_print ? ",\n" : ","); - } - o << string_t(new_indent, ' ') << "\"" - << escape_string(i->first) << "\":" - << (pretty_print ? " " : ""); - i->second.dump(o, pretty_print, indent_step, new_indent); - } - - // decrease indentation - if (pretty_print) - { - new_indent -= indent_step; - o << "\n"; - } - - o << string_t(new_indent, ' ') + "}"; - return; - } - - case value_t::array: - { - assert(m_value.array != nullptr); - - if (m_value.array->empty()) - { - o << "[]"; - return; - } - - o << "["; - - // increase indentation - if (pretty_print) - { - new_indent += indent_step; - o << "\n"; - } - - for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i) - { - if (i != m_value.array->cbegin()) - { - o << (pretty_print ? ",\n" : ","); - } - o << string_t(new_indent, ' '); - i->dump(o, pretty_print, indent_step, new_indent); - } - - // decrease indentation - if (pretty_print) - { - new_indent -= indent_step; - o << "\n"; - } - - o << string_t(new_indent, ' ') << "]"; - return; - } - - case value_t::string: - { - assert(m_value.string != nullptr); - o << string_t("\"") << escape_string(*m_value.string) << "\""; - return; - } - - case value_t::boolean: - { - o << (m_value.boolean ? "true" : "false"); - return; - } - - case value_t::number_integer: - { - o << m_value.number_integer; - return; - } - - case value_t::number_float: - { - // If the number is an integer then output as a fixed with with - // precision 1 to output "0.0", "1.0" etc as expected for some - // round trip tests otherwise 15 digits of precision allows - // round-trip IEEE 754 string->double->string; to be safe, we - // read this value from - // std::numeric_limits::digits10 - if (std::fmod(m_value.number_float, 1) == 0) - { - o << std::fixed << std::setprecision(1); - } - else - { - // std::defaultfloat not supported in gcc version < 5 - o.unsetf(std::ios_base::floatfield); - o << std::setprecision(std::numeric_limits::digits10); - } - o << m_value.number_float; - return; - } - - case value_t::discarded: - { - o << ""; - return; - } - - case value_t::null: - { - o << "null"; - return; - } - } - } - - private: - ////////////////////// - // member variables // - ////////////////////// - - /// the type of the current element - value_t m_type = value_t::null; - - /// the value of the current element - json_value m_value = {}; - - - private: - /////////////// - // iterators // - /////////////// - - /*! - @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 - { - public: - /// set iterator to a defined beginning - void set_begin() - { - m_it = begin_value; - } - - /// set iterator to a defined past the end - void set_end() - { - m_it = end_value; - } - - /// return whether the iterator can be dereferenced - bool is_begin() const - { - return (m_it == begin_value); - } - - /// return whether the iterator is at end - bool is_end() const - { - return (m_it == end_value); - } - - /// return reference to the value to change and compare - operator difference_type& () - { - return m_it; - } - - /// return value to compare - operator difference_type () const - { - return m_it; - } - - private: - 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::denorm_min(); - }; - - /*! - @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. - */ - struct internal_iterator - { - /// iterator for JSON objects - typename object_t::iterator object_iterator; - /// iterator for JSON arrays - typename array_t::iterator array_iterator; - /// generic iterator for all other types - primitive_iterator_t primitive_iterator; - - /// create an uninitialized internal_iterator - internal_iterator() - : object_iterator(), array_iterator(), primitive_iterator() - {} - }; - - /// proxy class for the iterator_wrapper functions - template - class iteration_proxy - { - private: - /// helper class for iteration - class iteration_proxy_internal - { - private: - /// the iterator - IteratorType anchor; - /// an index for arrays (used to create key names) - size_t array_index = 0; - - public: - iteration_proxy_internal(IteratorType it) - : anchor(it) - {} - - /// dereference operator (needed for range-based for) - iteration_proxy_internal& operator*() - { - return *this; - } - - /// increment operator (needed for range-based for) - iteration_proxy_internal& operator++() - { - ++anchor; - ++array_index; - - return *this; - } - - /// inequality operator (needed for range-based for) - bool operator!= (const iteration_proxy_internal& o) const - { - return anchor != o.anchor; - } - - /// return key of the iterator - typename basic_json::string_t key() const - { - assert(anchor.m_object != nullptr); - - switch (anchor.m_object->type()) - { - // use integer array index as key - case value_t::array: - { - return std::to_string(array_index); - } - - // use key from the object - case value_t::object: - { - return anchor.key(); - } - - // use an empty key for all primitive types - default: - { - return ""; - } - } - } - - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } - }; - - /// the container to iterate - typename IteratorType::reference container; - - public: - /// construct iteration proxy from a container - iteration_proxy(typename IteratorType::reference cont) - : container(cont) - {} - - /// return iterator begin (needed for range-based for) - iteration_proxy_internal begin() - { - return iteration_proxy_internal(container.begin()); - } - - /// return iterator end (needed for range-based for) - iteration_proxy_internal end() - { - return iteration_proxy_internal(container.end()); - } - }; - - public: - /*! - @brief a const random access iterator for the @ref basic_json class - - This class implements a const iterator for the @ref basic_json class. From - this class, the @ref iterator class is derived. - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - @since version 1.0.0 - */ - class const_iterator : public std::iterator - { - /// allow basic_json to access private members - friend class basic_json; - - public: - /// the type of the values when the iterator is dereferenced - using value_type = typename basic_json::value_type; - /// a type to represent differences between iterators - using difference_type = typename basic_json::difference_type; - /// defines a pointer to the type iterated over (value_type) - using pointer = typename basic_json::const_pointer; - /// defines a reference to the type iterated over (value_type) - using reference = typename basic_json::const_reference; - /// the category of the iterator - using iterator_category = std::bidirectional_iterator_tag; - - /// default constructor - const_iterator() = default; - - /// constructor for a given JSON instance - const_iterator(pointer object) : m_object(object) - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } - - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } - } - } - - /// copy constructor given a nonconst iterator - const_iterator(const iterator& other) : m_object(other.m_object) - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - m_it.object_iterator = other.m_it.object_iterator; - break; - } - - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } - - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; - } - } - } - - /// copy constructor - const_iterator(const const_iterator& other) noexcept - : m_object(other.m_object), m_it(other.m_it) - {} - - /// copy assignment - const_iterator& operator=(const_iterator other) noexcept( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - std::swap(m_object, other.m_object); - std::swap(m_it, other.m_it); - return *this; - } - - private: - /// set the iterator to the first value - void set_begin() - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_object->m_value.object != nullptr); - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } - - case basic_json::value_t::array: - { - assert(m_object->m_value.array != nullptr); - m_it.array_iterator = m_object->m_value.array->begin(); - break; - } - - case basic_json::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; - } - } - } - - /// set the iterator past the last value - void set_end() - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_object->m_value.object != nullptr); - m_it.object_iterator = m_object->m_value.object->end(); - break; - } - - case basic_json::value_t::array: - { - assert(m_object->m_value.array != nullptr); - m_it.array_iterator = m_object->m_value.array->end(); - break; - } - - default: - { - m_it.primitive_iterator.set_end(); - break; - } - } - } - - public: - /// return a reference to the value pointed to by the iterator - reference operator*() const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_object->m_value.object); - assert(m_it.object_iterator != m_object->m_value.object->end()); - return m_it.object_iterator->second; - } - - case basic_json::value_t::array: - { - assert(m_object->m_value.array); - assert(m_it.array_iterator != m_object->m_value.array->end()); - return *m_it.array_iterator; - } - - case basic_json::value_t::null: - { - throw std::out_of_range("cannot get value"); - } - - default: - { - if (m_it.primitive_iterator.is_begin()) - { - return *m_object; - } - else - { - throw std::out_of_range("cannot get value"); - } - } - } - } - - /// dereference the iterator - pointer operator->() const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - assert(m_object->m_value.object); - assert(m_it.object_iterator != m_object->m_value.object->end()); - return &(m_it.object_iterator->second); - } - - case basic_json::value_t::array: - { - assert(m_object->m_value.array); - assert(m_it.array_iterator != m_object->m_value.array->end()); - return &*m_it.array_iterator; - } - - default: - { - if (m_it.primitive_iterator.is_begin()) - { - return m_object; - } - else - { - throw std::out_of_range("cannot get value"); - } - } - } - } - - /// post-increment (it++) - const_iterator operator++(int) - { - auto result = *this; - ++(*this); - return result; - } - - /// pre-increment (++it) - const_iterator& operator++() - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - ++m_it.object_iterator; - break; - } - - case basic_json::value_t::array: - { - ++m_it.array_iterator; - break; - } - - default: - { - ++m_it.primitive_iterator; - break; - } - } - - return *this; - } - - /// post-decrement (it--) - const_iterator operator--(int) - { - auto result = *this; - --(*this); - return result; - } - - /// pre-decrement (--it) - const_iterator& operator--() - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - --m_it.object_iterator; - break; - } - - case basic_json::value_t::array: - { - --m_it.array_iterator; - break; - } - - default: - { - --m_it.primitive_iterator; - break; - } - } - - return *this; - } - - /// comparison: equal - bool operator==(const const_iterator& other) const - { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { - throw std::domain_error("cannot compare iterators of different containers"); - } - - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - return (m_it.object_iterator == other.m_it.object_iterator); - } - - case basic_json::value_t::array: - { - return (m_it.array_iterator == other.m_it.array_iterator); - } - - default: - { - return (m_it.primitive_iterator == other.m_it.primitive_iterator); - } - } - } - - /// comparison: not equal - bool operator!=(const const_iterator& other) const - { - return not operator==(other); - } - - /// comparison: smaller - bool operator<(const const_iterator& other) const - { - // if objects are not the same, the comparison is undefined - if (m_object != other.m_object) - { - throw std::domain_error("cannot compare iterators of different containers"); - } - - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - throw std::domain_error("cannot compare order of object iterators"); - } - - case basic_json::value_t::array: - { - return (m_it.array_iterator < other.m_it.array_iterator); - } - - default: - { - return (m_it.primitive_iterator < other.m_it.primitive_iterator); - } - } - } - - /// comparison: less than or equal - bool operator<=(const const_iterator& other) const - { - return not other.operator < (*this); - } - - /// comparison: greater than - bool operator>(const const_iterator& other) const - { - return not operator<=(other); - } - - /// comparison: greater than or equal - bool operator>=(const const_iterator& other) const - { - return not operator<(other); - } - - /// add to iterator - const_iterator& operator+=(difference_type i) - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - throw std::domain_error("cannot use offsets with object iterators"); - } - - case basic_json::value_t::array: - { - m_it.array_iterator += i; - break; - } - - default: - { - m_it.primitive_iterator += i; - break; - } - } - - return *this; - } - - /// subtract from iterator - const_iterator& operator-=(difference_type i) - { - return operator+=(-i); - } - - /// add to iterator - const_iterator operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - const_iterator operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - /// return difference - difference_type operator-(const const_iterator& other) const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - throw std::domain_error("cannot use offsets with object iterators"); - } - - case basic_json::value_t::array: - { - return m_it.array_iterator - other.m_it.array_iterator; - } - - default: - { - return m_it.primitive_iterator - other.m_it.primitive_iterator; - } - } - } - - /// access to successor - reference operator[](difference_type n) const - { - assert(m_object != nullptr); - - switch (m_object->m_type) - { - case basic_json::value_t::object: - { - throw std::domain_error("cannot use operator[] for object iterators"); - } - - case basic_json::value_t::array: - { - return *(m_it.array_iterator + n); - } - - case basic_json::value_t::null: - { - throw std::out_of_range("cannot get value"); - } - - default: - { - if (m_it.primitive_iterator == -n) - { - return *m_object; - } - else - { - throw std::out_of_range("cannot get value"); - } - } - } - } - - /// return the key of an object iterator - typename object_t::key_type key() const - { - assert(m_object != nullptr); - - if (m_object->is_object()) - { - return m_it.object_iterator->first; - } - else - { - throw std::domain_error("cannot use key() for non-object iterators"); - } - } - - /// return the value of an iterator - reference value() const - { - return operator*(); - } - - private: - /// associated JSON instance - pointer m_object = nullptr; - /// the actual iterator of the associated instance - internal_iterator m_it = internal_iterator(); - }; - - /*! - @brief a mutable random access iterator for the @ref basic_json class - - @requirement The class satisfies the following concept requirements: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element. - - @since version 1.0.0 - */ - class iterator : public const_iterator - { - public: - using base_iterator = const_iterator; - using pointer = typename basic_json::pointer; - using reference = typename basic_json::reference; - - /// default constructor - iterator() = default; - - /// constructor for a given JSON instance - iterator(pointer object) noexcept - : base_iterator(object) - {} - - /// copy constructor - iterator(const iterator& other) noexcept - : base_iterator(other) - {} - - /// copy assignment - iterator& operator=(iterator other) noexcept( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - base_iterator::operator=(other); - return *this; - } - - /// return a reference to the value pointed to by the iterator - reference operator*() - { - return const_cast(base_iterator::operator*()); - } - - /// dereference the iterator - pointer operator->() - { - return const_cast(base_iterator::operator->()); - } - - /// post-increment (it++) - iterator operator++(int) - { - iterator result = *this; - base_iterator::operator++(); - return result; - } - - /// pre-increment (++it) - iterator& operator++() - { - base_iterator::operator++(); - return *this; - } - - /// post-decrement (it--) - iterator operator--(int) - { - iterator result = *this; - base_iterator::operator--(); - return result; - } - - /// pre-decrement (--it) - iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - - /// add to iterator - iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } - - /// subtract from iterator - iterator& operator-=(difference_type i) - { - base_iterator::operator-=(i); - return *this; - } - - /// add to iterator - iterator operator+(difference_type i) - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - iterator operator-(difference_type i) - { - auto result = *this; - result -= i; - return result; - } - - difference_type operator-(const iterator& other) const - { - return base_iterator::operator-(other); - } - - /// access to successor - reference operator[](difference_type n) const - { - return const_cast(base_iterator::operator[](n)); - } - - /// return the value of an iterator - reference value() const - { - return const_cast(base_iterator::value()); - } - }; - - /*! - @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: - - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): - The iterator that can be moved to point (forward and backward) to any - element in constant time. - - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): - It is possible to write to the pointed-to element (only if @a Base is - @ref iterator). - - @since version 1.0.0 - */ - template - class json_reverse_iterator : public std::reverse_iterator - { - public: - /// shortcut to the reverse iterator adaptor - using base_iterator = std::reverse_iterator; - /// the reference type for the pointed-to element - using reference = typename Base::reference; - - /// create reverse iterator from iterator - json_reverse_iterator(const typename base_iterator::iterator_type& it) - : base_iterator(it) - {} - - /// create reverse iterator from base class - json_reverse_iterator(const base_iterator& it) - : base_iterator(it) - {} - - /// post-increment (it++) - json_reverse_iterator operator++(int) - { - return base_iterator::operator++(1); - } - - /// pre-increment (++it) - json_reverse_iterator& operator++() - { - base_iterator::operator++(); - return *this; - } - - /// post-decrement (it--) - json_reverse_iterator operator--(int) - { - return base_iterator::operator--(1); - } - - /// pre-decrement (--it) - json_reverse_iterator& operator--() - { - base_iterator::operator--(); - return *this; - } - - /// add to iterator - json_reverse_iterator& operator+=(difference_type i) - { - base_iterator::operator+=(i); - return *this; - } - - /// add to iterator - json_reverse_iterator operator+(difference_type i) const - { - auto result = *this; - result += i; - return result; - } - - /// subtract from iterator - json_reverse_iterator operator-(difference_type i) const - { - auto result = *this; - result -= i; - return result; - } - - /// return difference - difference_type operator-(const json_reverse_iterator& other) const - { - return this->base() - other.base(); - } - - /// access to successor - reference operator[](difference_type n) const - { - return *(this->operator+(n)); - } - - /// return the key of an object iterator - typename object_t::key_type key() const - { - auto it = --this->base(); - return it.key(); - } - - /// return the value of an iterator - reference value() const - { - auto it = --this->base(); - return it.operator * (); - } - }; - - - private: - ////////////////////// - // lexer and parser // - ////////////////////// - - /*! - @brief lexical analysis - - This class organizes the lexical analysis during JSON deserialization. The - core of it is a scanner generated by re2c that processes - a buffer and recognizes tokens according to RFC 7159. - */ - class lexer - { - public: - /// token types for the parser - enum class token_type - { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the "true" literal - literal_false, ///< the "false" literal - literal_null, ///< the "null" literal - value_string, ///< a string -- use get_string() for actual value - value_number, ///< a number -- use get_number() for actual value - begin_array, ///< the character for array begin "[" - begin_object, ///< the character for object begin "{" - end_array, ///< the character for array end "]" - end_object, ///< the character for object end "}" - name_separator, ///< the name separator ":" - value_separator, ///< the value separator "," - parse_error, ///< indicating a parse error - end_of_input ///< indicating the end of the input buffer - }; - - /// the char type to use in the lexer - using lexer_char_t = unsigned char; - - /// constructor with a given buffer - explicit lexer(const string_t& s) noexcept - : m_stream(nullptr), m_buffer(s) - { - m_content = reinterpret_cast(s.c_str()); - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + s.size(); - } - - /// constructor with a given stream - explicit lexer(std::istream* s) noexcept - : m_stream(s), m_buffer() - { - assert(m_stream != nullptr); - getline(*m_stream, m_buffer); - m_content = reinterpret_cast(m_buffer.c_str()); - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + m_buffer.size(); - } - - /// default constructor - lexer() = default; - - // switch off unwanted functions - lexer(const lexer&) = delete; - lexer operator=(const lexer&) = delete; - - /*! - @brief create a string from a Unicode code point - - @param[in] codepoint1 the code point (can be high surrogate) - @param[in] codepoint2 the code point (can be low surrogate or 0) - - @return string representation of the code point - - @throw std::out_of_range if code point is >0x10ffff; example: `"code - points above 0x10FFFF are invalid"` - @throw std::invalid_argument if the low surrogate is invalid; example: - `""missing or wrong low surrogate""` - - @see - */ - static string_t to_unicode(const std::size_t codepoint1, - const std::size_t codepoint2 = 0) - { - string_t result; - - // calculate the codepoint from the given code points - std::size_t codepoint = codepoint1; - - // check if codepoint1 is a high surrogate - if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) - { - // check if codepoint2 is a low surrogate - if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF) - { - codepoint = - // high surrogate occupies the most significant 22 bits - (codepoint1 << 10) - // low surrogate occupies the least significant 15 bits - + codepoint2 - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00; - } - else - { - throw std::invalid_argument("missing or wrong low surrogate"); - } - } - - if (codepoint < 0x80) - { - // 1-byte characters: 0xxxxxxx (ASCII) - result.append(1, static_cast(codepoint)); - } - else if (codepoint <= 0x7ff) - { - // 2-byte characters: 110xxxxx 10xxxxxx - result.append(1, static_cast(0xC0 | ((codepoint >> 6) & 0x1F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0xffff) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast(0xE0 | ((codepoint >> 12) & 0x0F))); - result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else if (codepoint <= 0x10ffff) - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - result.append(1, static_cast(0xF0 | ((codepoint >> 18) & 0x07))); - result.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); - result.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); - result.append(1, static_cast(0x80 | (codepoint & 0x3F))); - } - else - { - throw std::out_of_range("code points above 0x10FFFF are invalid"); - } - - return result; - } - - /// return name of values of type token_type (only used for errors) - static std::string token_type_name(token_type t) - { - switch (t) - { - case token_type::uninitialized: - return ""; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case token_type::value_number: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return ""; - case token_type::end_of_input: - return "end of input"; - default: - { - // catch non-enum values - return "unknown token"; // LCOV_EXCL_LINE - } - } - } - - /*! - This function implements a scanner for JSON. It is specified using - regular expressions that try to follow RFC 7159 as close as possible. - These regular expressions are then translated into a deterministic - finite automaton (DFA) by the tool re2c . As a result, - the translated code for this function consists of a large block of code - with goto jumps. - - @return the class of the next token read from the buffer - */ - token_type scan() noexcept - { - // pointer for backtracking information - m_marker = nullptr; - - // remember the begin of the token - m_start = m_cursor; - assert(m_start != nullptr); - - - { - lexer_char_t yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = - { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 32, 32, 0, 0, 32, 0, 0, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 96, 64, 0, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 0, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - }; - if ((m_limit - m_cursor) < 5) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= ':') - { - if (yych <= ' ') - { - if (yych <= '\n') - { - if (yych <= 0x00) - { - goto basic_json_parser_28; - } - if (yych <= 0x08) - { - goto basic_json_parser_30; - } - if (yych >= '\n') - { - goto basic_json_parser_4; - } - } - else - { - if (yych == '\r') - { - goto basic_json_parser_2; - } - if (yych <= 0x1F) - { - goto basic_json_parser_30; - } - } - } - else - { - if (yych <= ',') - { - if (yych == '"') - { - goto basic_json_parser_27; - } - if (yych <= '+') - { - goto basic_json_parser_30; - } - goto basic_json_parser_16; - } - else - { - if (yych <= '/') - { - if (yych <= '-') - { - goto basic_json_parser_23; - } - goto basic_json_parser_30; - } - else - { - if (yych <= '0') - { - goto basic_json_parser_24; - } - if (yych <= '9') - { - goto basic_json_parser_26; - } - goto basic_json_parser_18; - } - } - } - } - else - { - if (yych <= 'n') - { - if (yych <= ']') - { - if (yych == '[') - { - goto basic_json_parser_8; - } - if (yych <= '\\') - { - goto basic_json_parser_30; - } - goto basic_json_parser_10; - } - else - { - if (yych == 'f') - { - goto basic_json_parser_22; - } - if (yych <= 'm') - { - goto basic_json_parser_30; - } - goto basic_json_parser_20; - } - } - else - { - if (yych <= '{') - { - if (yych == 't') - { - goto basic_json_parser_21; - } - if (yych <= 'z') - { - goto basic_json_parser_30; - } - goto basic_json_parser_12; - } - else - { - if (yych <= '}') - { - if (yych <= '|') - { - goto basic_json_parser_30; - } - goto basic_json_parser_14; - } - else - { - if (yych == 0xEF) - { - goto basic_json_parser_6; - } - goto basic_json_parser_30; - } - } - } - } -basic_json_parser_2: - ++m_cursor; - yych = *m_cursor; - goto basic_json_parser_5; -basic_json_parser_3: - { - return scan(); - } -basic_json_parser_4: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; -basic_json_parser_5: - if (yybm[0 + yych] & 32) - { - goto basic_json_parser_4; - } - goto basic_json_parser_3; -basic_json_parser_6: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 0xBB) - { - goto basic_json_parser_64; - } -basic_json_parser_7: - { - return token_type::parse_error; - } -basic_json_parser_8: - ++m_cursor; - { - return token_type::begin_array; - } -basic_json_parser_10: - ++m_cursor; - { - return token_type::end_array; - } -basic_json_parser_12: - ++m_cursor; - { - return token_type::begin_object; - } -basic_json_parser_14: - ++m_cursor; - { - return token_type::end_object; - } -basic_json_parser_16: - ++m_cursor; - { - return token_type::value_separator; - } -basic_json_parser_18: - ++m_cursor; - { - return token_type::name_separator; - } -basic_json_parser_20: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'u') - { - goto basic_json_parser_60; - } - goto basic_json_parser_7; -basic_json_parser_21: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'r') - { - goto basic_json_parser_56; - } - goto basic_json_parser_7; -basic_json_parser_22: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych == 'a') - { - goto basic_json_parser_51; - } - goto basic_json_parser_7; -basic_json_parser_23: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_7; - } - if (yych <= '0') - { - goto basic_json_parser_50; - } - if (yych <= '9') - { - goto basic_json_parser_41; - } - goto basic_json_parser_7; -basic_json_parser_24: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') - { - if (yych == '.') - { - goto basic_json_parser_43; - } - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - } -basic_json_parser_25: - { - return token_type::value_number; - } -basic_json_parser_26: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - goto basic_json_parser_42; -basic_json_parser_27: - yyaccept = 0; - yych = *(m_marker = ++m_cursor); - if (yych <= 0x0F) - { - goto basic_json_parser_7; - } - goto basic_json_parser_32; -basic_json_parser_28: - ++m_cursor; - { - return token_type::end_of_input; - } -basic_json_parser_30: - yych = *++m_cursor; - goto basic_json_parser_7; -basic_json_parser_31: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; -basic_json_parser_32: - if (yybm[0 + yych] & 64) - { - goto basic_json_parser_31; - } - if (yych <= 0x0F) - { - goto basic_json_parser_33; - } - if (yych <= '"') - { - goto basic_json_parser_35; - } - goto basic_json_parser_34; -basic_json_parser_33: - m_cursor = m_marker; - if (yyaccept == 0) - { - goto basic_json_parser_7; - } - else - { - goto basic_json_parser_25; - } -basic_json_parser_34: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= 'e') - { - if (yych <= '/') - { - if (yych == '"') - { - goto basic_json_parser_31; - } - if (yych <= '.') - { - goto basic_json_parser_33; - } - goto basic_json_parser_31; - } - else - { - if (yych <= '\\') - { - if (yych <= '[') - { - goto basic_json_parser_33; - } - goto basic_json_parser_31; - } - else - { - if (yych == 'b') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - } - } - else - { - if (yych <= 'q') - { - if (yych <= 'f') - { - goto basic_json_parser_31; - } - if (yych == 'n') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 's') - { - if (yych <= 'r') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 't') - { - goto basic_json_parser_31; - } - if (yych <= 'u') - { - goto basic_json_parser_37; - } - goto basic_json_parser_33; - } - } - } -basic_json_parser_35: - ++m_cursor; - { - return token_type::value_string; - } -basic_json_parser_37: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_38; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych >= 'g') - { - goto basic_json_parser_33; - } - } -basic_json_parser_38: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_39; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych >= 'g') - { - goto basic_json_parser_33; - } - } -basic_json_parser_39: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_40; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych >= 'g') - { - goto basic_json_parser_33; - } - } -basic_json_parser_40: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '@') - { - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } - else - { - if (yych <= 'F') - { - goto basic_json_parser_31; - } - if (yych <= '`') - { - goto basic_json_parser_33; - } - if (yych <= 'f') - { - goto basic_json_parser_31; - } - goto basic_json_parser_33; - } -basic_json_parser_41: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; -basic_json_parser_42: - if (yybm[0 + yych] & 128) - { - goto basic_json_parser_41; - } - if (yych <= 'D') - { - if (yych != '.') - { - goto basic_json_parser_25; - } - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_25; - } -basic_json_parser_43: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_48; - } - goto basic_json_parser_33; -basic_json_parser_44: - yych = *++m_cursor; - if (yych <= ',') - { - if (yych != '+') - { - goto basic_json_parser_33; - } - } - else - { - if (yych <= '-') - { - goto basic_json_parser_45; - } - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych <= '9') - { - goto basic_json_parser_46; - } - goto basic_json_parser_33; - } -basic_json_parser_45: - yych = *++m_cursor; - if (yych <= '/') - { - goto basic_json_parser_33; - } - if (yych >= ':') - { - goto basic_json_parser_33; - } -basic_json_parser_46: - ++m_cursor; - if (m_limit <= m_cursor) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= '/') - { - goto basic_json_parser_25; - } - if (yych <= '9') - { - goto basic_json_parser_46; - } - goto basic_json_parser_25; -basic_json_parser_48: - yyaccept = 1; - m_marker = ++m_cursor; - if ((m_limit - m_cursor) < 3) - { - yyfill(); // LCOV_EXCL_LINE; - } - yych = *m_cursor; - if (yych <= 'D') - { - if (yych <= '/') - { - goto basic_json_parser_25; - } - if (yych <= '9') - { - goto basic_json_parser_48; - } - goto basic_json_parser_25; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_25; - } -basic_json_parser_50: - yyaccept = 1; - yych = *(m_marker = ++m_cursor); - if (yych <= 'D') - { - if (yych == '.') - { - goto basic_json_parser_43; - } - goto basic_json_parser_25; - } - else - { - if (yych <= 'E') - { - goto basic_json_parser_44; - } - if (yych == 'e') - { - goto basic_json_parser_44; - } - goto basic_json_parser_25; - } -basic_json_parser_51: - yych = *++m_cursor; - if (yych != 'l') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 's') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 'e') - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return token_type::literal_false; - } -basic_json_parser_56: - yych = *++m_cursor; - if (yych != 'u') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 'e') - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return token_type::literal_true; - } -basic_json_parser_60: - yych = *++m_cursor; - if (yych != 'l') - { - goto basic_json_parser_33; - } - yych = *++m_cursor; - if (yych != 'l') - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return token_type::literal_null; - } -basic_json_parser_64: - yych = *++m_cursor; - if (yych != 0xBF) - { - goto basic_json_parser_33; - } - ++m_cursor; - { - return scan(); - } - } - - - } - - /// append data from the stream to the internal buffer - void yyfill() noexcept - { - if (m_stream == nullptr or not * m_stream) - { - return; - } - - const ssize_t offset_start = m_start - m_content; - const ssize_t offset_marker = m_marker - m_start; - const ssize_t offset_cursor = m_cursor - m_start; - - m_buffer.erase(0, static_cast(offset_start)); - std::string line; - assert(m_stream != nullptr); - std::getline(*m_stream, line); - m_buffer += "\n" + line; // add line with newline symbol - - m_content = reinterpret_cast(m_buffer.c_str()); - assert(m_content != nullptr); - m_start = m_content; - m_marker = m_start + offset_marker; - m_cursor = m_start + offset_cursor; - m_limit = m_start + m_buffer.size() - 1; - } - - /// return string representation of last read token - string_t get_token() const noexcept - { - assert(m_start != nullptr); - return string_t(reinterpret_cast(m_start), - static_cast(m_cursor - m_start)); - } - - /*! - @brief return string value for string tokens - - The function iterates the characters between the opening and closing - quotes of the string value. The complete string is the range - [m_start,m_cursor). Consequently, we iterate from m_start+1 to - m_cursor-1. - - We differentiate two cases: - - 1. Escaped characters. In this case, a new character is constructed - according to the nature of the escape. Some escapes create new - characters (e.g., @c "\\n" is replaced by @c "\n"), some are copied - as is (e.g., @c "\\\\"). Furthermore, Unicode escapes of the shape - @c "\\uxxxx" need special care. In this case, to_unicode takes care - of the construction of the values. - 2. Unescaped characters are copied as is. - - @return string value of current token without opening and closing quotes - @throw std::out_of_range if to_unicode fails - */ - string_t get_string() const - { - string_t result; - result.reserve(static_cast(m_cursor - m_start - 2)); - - // iterate the result between the quotes - for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i) - { - // process escaped characters - if (*i == '\\') - { - // read next character - ++i; - - switch (*i) - { - // the default escapes - case 't': - { - result += "\t"; - break; - } - case 'b': - { - result += "\b"; - break; - } - case 'f': - { - result += "\f"; - break; - } - case 'n': - { - result += "\n"; - break; - } - case 'r': - { - result += "\r"; - break; - } - case '\\': - { - result += "\\"; - break; - } - case '/': - { - result += "/"; - break; - } - case '"': - { - result += "\""; - break; - } - - // unicode - case 'u': - { - // get code xxxx from uxxxx - auto codepoint = std::strtoul(std::string(reinterpret_cast(i + 1), - 4).c_str(), nullptr, 16); - - // check if codepoint is a high surrogate - if (codepoint >= 0xD800 and codepoint <= 0xDBFF) - { - // make sure there is a subsequent unicode - if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u') - { - throw std::invalid_argument("missing low surrogate"); - } - - // get code yyyy from uxxxx\uyyyy - auto codepoint2 = std::strtoul(std::string(reinterpret_cast - (i + 7), 4).c_str(), nullptr, 16); - result += to_unicode(codepoint, codepoint2); - // skip the next 10 characters (xxxx\uyyyy) - i += 10; - } - else - { - // add unicode character(s) - result += to_unicode(codepoint); - // skip the next four characters (xxxx) - i += 4; - } - break; - } - } - } - else - { - // all other characters are just copied to the end of the - // string - result.append(1, static_cast(*i)); - } - } - - return result; - } - - /*! - @brief parse floating point number - - This function (and its overloads) serves to select the most approprate - standard floating point number parsing function (i.e., `std::strtof`, - `std::strtod`, or `std::strtold`) based on the type supplied via the - first parameter. Set this to @a static_cast(nullptr). - - @param[in] type the @ref number_float_t in use - - @param[in,out] endptr recieves a pointer to the first character after - the number - - @return the floating point number - - @bug This function uses `std::strtof`, `std::strtod`, or `std::strtold` - which use the current C locale to determine which character is used as - decimal point character. This may yield to parse errors if the locale - does not used `.`. - */ - long double str_to_float_t(long double* /* type */, char** endptr) const - { - return std::strtold(reinterpret_cast(m_start), endptr); - } - - /// @copydoc str_to_float_t - double str_to_float_t(double*, char** endptr) const - { - return std::strtod(reinterpret_cast(m_start), endptr); - } - - /// @copydoc str_to_float_t - float str_to_float_t(float*, char** endptr) const - { - return std::strtof(reinterpret_cast(m_start), endptr); - } - - /*! - @brief return number value for number tokens - - This function translates the last token into a floating point number. - The pointer m_start points to the beginning of the parsed number. We - pass this pointer to std::strtod which sets endptr to the first - character past the converted number. If this pointer is not the same as - m_cursor, then either more or less characters have been used during the - comparison. This can happen for inputs like "01" which will be treated - like number 0 followed by number 1. - - @return the result of the number conversion or NAN if the conversion - read past the current token. The latter case needs to be treated by the - caller function. - - @throw std::range_error if passed value is out of range - */ - number_float_t get_number() const - { - // conversion - typename string_t::value_type* endptr; - assert(m_start != nullptr); - number_float_t float_val = str_to_float_t(static_cast(nullptr), &endptr); - - // return float_val if the whole number was translated and NAN - // otherwise - return (reinterpret_cast(endptr) == m_cursor) ? float_val : NAN; - } - - private: - /// optional input stream - std::istream* m_stream = nullptr; - /// the buffer - string_t m_buffer; - /// the buffer pointer - const lexer_char_t* m_content = nullptr; - /// pointer to the beginning of the current symbol - const lexer_char_t* m_start = nullptr; - /// pointer for backtracking information - const lexer_char_t* m_marker = nullptr; - /// pointer to the current symbol - const lexer_char_t* m_cursor = nullptr; - /// pointer to the end of the buffer - const lexer_char_t* m_limit = nullptr; - }; - - /*! - @brief syntax analysis - - This class implements a recursive decent parser. - */ - class parser - { - public: - /// constructor for strings - parser(const string_t& s, parser_callback_t cb = nullptr) - : callback(cb), m_lexer(s) - { - // read first token - get_token(); - } - - /// a parser reading from an input stream - parser(std::istream& _is, parser_callback_t cb = nullptr) - : callback(cb), m_lexer(&_is) - { - // read first token - get_token(); - } - - /// public parser interface - basic_json parse() - { - basic_json result = parse_internal(true); - - expect(lexer::token_type::end_of_input); - - // return parser result and replace it with null in case the - // top-level value was discarded by the callback function - return result.is_discarded() ? basic_json() : result; - } - - private: - /// the actual parser - basic_json parse_internal(bool keep) - { - auto result = basic_json(value_t::discarded); - - switch (last_token) - { - case lexer::token_type::begin_object: - { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) - { - // explicitly set result to object to cope with {} - result.m_type = value_t::object; - result.m_value = json_value(value_t::object); - } - - // read next token - get_token(); - - // closing } -> we are done - if (last_token == lexer::token_type::end_object) - { - get_token(); - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } - - // no comma is expected here - unexpect(lexer::token_type::value_separator); - - // otherwise: parse key-value pairs - do - { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) - { - get_token(); - } - - // store key - expect(lexer::token_type::value_string); - const auto key = m_lexer.get_string(); - - bool keep_tag = false; - if (keep) - { - if (callback) - { - basic_json k(key); - keep_tag = callback(depth, parse_event_t::key, k); - } - else - { - keep_tag = true; - } - } - - // parse separator (:) - get_token(); - expect(lexer::token_type::name_separator); - - // parse and add value - get_token(); - auto value = parse_internal(keep); - if (keep and keep_tag and not value.is_discarded()) - { - result[key] = std::move(value); - } - } - while (last_token == lexer::token_type::value_separator); - - // closing } - expect(lexer::token_type::end_object); - get_token(); - if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) - { - result = basic_json(value_t::discarded); - } - - return result; - } - - case lexer::token_type::begin_array: - { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) - { - // explicitly set result to object to cope with [] - result.m_type = value_t::array; - result.m_value = json_value(value_t::array); - } - - // read next token - get_token(); - - // closing ] -> we are done - if (last_token == lexer::token_type::end_array) - { - get_token(); - if (callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } - - // no comma is expected here - unexpect(lexer::token_type::value_separator); - - // otherwise: parse values - do - { - // ugly, but could be fixed with loop reorganization - if (last_token == lexer::token_type::value_separator) - { - get_token(); - } - - // parse value - auto value = parse_internal(keep); - if (keep and not value.is_discarded()) - { - result.push_back(std::move(value)); - } - } - while (last_token == lexer::token_type::value_separator); - - // closing ] - expect(lexer::token_type::end_array); - get_token(); - if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) - { - result = basic_json(value_t::discarded); - } - - return result; - } - - case lexer::token_type::literal_null: - { - get_token(); - result.m_type = value_t::null; - break; - } - - case lexer::token_type::value_string: - { - const auto s = m_lexer.get_string(); - get_token(); - result = basic_json(s); - break; - } - - case lexer::token_type::literal_true: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = true; - break; - } - - case lexer::token_type::literal_false: - { - get_token(); - result.m_type = value_t::boolean; - result.m_value = false; - break; - } - - case lexer::token_type::value_number: - { - result.m_value = m_lexer.get_number(); - - // NAN is returned if token could not be translated - // completely - if (std::isnan(result.m_value.number_float)) - { - throw std::invalid_argument(std::string("parse error - ") + - m_lexer.get_token() + " is not a number"); - } - - get_token(); - - // check if conversion loses precision (special case -0.0 always loses precision) - const auto int_val = static_cast(result.m_value.number_float); - if (result.m_value.number_float == static_cast(int_val) and - result.m_value.number_integer != json_value(-0.0f).number_integer) - { - // we would not lose precision -> return int - result.m_type = value_t::number_integer; - result.m_value = int_val; - } - else - { - // we would lose precision -> return float - result.m_type = value_t::number_float; - } - break; - } - - default: - { - // the last token was unexpected - unexpect(last_token); - } - } - - if (keep and callback and not callback(depth, parse_event_t::value, result)) - { - result = basic_json(value_t::discarded); - } - return result; - } - - /// get next token from lexer - typename lexer::token_type get_token() - { - last_token = m_lexer.scan(); - return last_token; - } - - void expect(typename lexer::token_type t) const - { - if (t != last_token) - { - std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : - lexer::token_type_name(last_token)); - error_msg += "; expected " + lexer::token_type_name(t); - throw std::invalid_argument(error_msg); - } - } - - void unexpect(typename lexer::token_type t) const - { - if (t == last_token) - { - std::string error_msg = "parse error - unexpected "; - error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") : - lexer::token_type_name(last_token)); - throw std::invalid_argument(error_msg); - } - } - - private: - /// current level of recursion - int depth = 0; - /// callback function - parser_callback_t callback; - /// the type of the last read token - typename lexer::token_type last_token = lexer::token_type::uninitialized; - /// the lexer - lexer m_lexer; - }; -}; - - -///////////// -// presets // -///////////// - -/*! -@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<>; -} - - -///////////////////////// -// nonmember functions // -///////////////////////// - -// specialization of std::swap, and std::hash -namespace std -{ -/*! -@brief exchanges the values of two JSON objects - -@since version 1.0.0 -*/ -template <> -inline void swap(nlohmann::json& j1, - nlohmann::json& j2) noexcept( - is_nothrow_move_constructible::value and - is_nothrow_move_assignable::value - ) -{ - j1.swap(j2); -} - -/// hash value for JSON objects -template <> -struct hash -{ - /*! - @brief return a hash value for a JSON object - - @since version 1.0.0 - */ - std::size_t operator()(const nlohmann::json& j) const - { - // a naive hashing via the string representation - const auto& h = hash(); - return h(j.dump()); - } -}; -} - -/*! -@brief user-defined string literal for JSON values - -This operator implements a user-defined string literal for JSON objects. It can -be used by adding \p "_json" to a string literal and returns a JSON object if -no parse error occurred. - -@param[in] s a string representation of a JSON object -@return a JSON object - -@since version 1.0.0 -*/ -inline nlohmann::json operator "" _json(const char* s, std::size_t) -{ - return nlohmann::json::parse(reinterpret_cast(s)); -} - -// restore GCC/clang diagnostic settings -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #pragma GCC diagnostic pop -#endif - -#endif diff --git a/modules/iswa/rendering/datacygnet.cpp b/modules/iswa/rendering/datacygnet.cpp index e5623ac8b4..2604cc6574 100644 --- a/modules/iswa/rendering/datacygnet.cpp +++ b/modules/iswa/rendering/datacygnet.cpp @@ -229,7 +229,7 @@ void DataCygnet::setTextureUniforms() { _shader->setUniform("transferFunctions[0]", tfUnits[0]); } else { for (int option : selectedOptions) { - if (_transferFunctions.size() >= option) { + if (static_cast(_transferFunctions.size()) >= option) { tfUnits[j].activate(); _transferFunctions[option].bind(); _shader->setUniform( diff --git a/modules/iswa/rendering/iswabasegroup.cpp b/modules/iswa/rendering/iswabasegroup.cpp index 5d96443b3c..7df02b20e5 100644 --- a/modules/iswa/rendering/iswabasegroup.cpp +++ b/modules/iswa/rendering/iswabasegroup.cpp @@ -24,7 +24,7 @@ #include -#include +#include #include namespace { diff --git a/modules/iswa/rendering/iswadatagroup.cpp b/modules/iswa/rendering/iswadatagroup.cpp index c1f2674ac6..35c0b65f39 100644 --- a/modules/iswa/rendering/iswadatagroup.cpp +++ b/modules/iswa/rendering/iswadatagroup.cpp @@ -24,7 +24,6 @@ #include -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include namespace { diff --git a/modules/iswa/rendering/iswakameleongroup.cpp b/modules/iswa/rendering/iswakameleongroup.cpp index bc2c34c839..da112f6321 100644 --- a/modules/iswa/rendering/iswakameleongroup.cpp +++ b/modules/iswa/rendering/iswakameleongroup.cpp @@ -24,8 +24,8 @@ #include -#include #include +#include #include #include #include diff --git a/modules/iswa/rendering/kameleonplane.cpp b/modules/iswa/rendering/kameleonplane.cpp index d46ee10a8f..a662689a8e 100644 --- a/modules/iswa/rendering/kameleonplane.cpp +++ b/modules/iswa/rendering/kameleonplane.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -38,20 +39,6 @@ #include #include - #ifdef __clang__ -#elif defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif // __GNUC__ - -#include - -#ifdef __clang__ - -#elif defined(__GNUC__) -#pragma GCC diagnostic pop -#endif // __GNUC__ - namespace { using json = nlohmann::json; constexpr const char* _loggerCat = "KameleonPlane"; diff --git a/modules/iswa/util/dataprocessorjson.cpp b/modules/iswa/util/dataprocessorjson.cpp index 945b0c5a4d..942a14b2e1 100644 --- a/modules/iswa/util/dataprocessorjson.cpp +++ b/modules/iswa/util/dataprocessorjson.cpp @@ -24,7 +24,7 @@ #include -#include +#include #include #include diff --git a/modules/iswa/util/iswamanager.cpp b/modules/iswa/util/iswamanager.cpp index 7166ca9a50..6219f37fbe 100644 --- a/modules/iswa/util/iswamanager.cpp +++ b/modules/iswa/util/iswamanager.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -45,25 +46,6 @@ #include "iswamanager_lua.inl" -#ifdef __clang__ -#pragma clang diagnostic push -// #pragma clang diagnostic ignored "-Wuseless-cast" -// #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#elif (defined __GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuseless-cast" -#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif // __clang__ - -#include - -#ifdef __clang__ -#pragma clang diagnostic pop -#elif (defined __GNUC__) -#pragma GCC diagnostic pop -#endif // __clang__ - - #ifdef OPENSPACE_MODULE_KAMELEON_ENABLED #ifdef WIN32 diff --git a/modules/kameleon/src/kameleonwrapper.cpp b/modules/kameleon/src/kameleonwrapper.cpp index b6fa34b6ef..abfe554825 100644 --- a/modules/kameleon/src/kameleonwrapper.cpp +++ b/modules/kameleon/src/kameleonwrapper.cpp @@ -731,7 +731,7 @@ glm::vec4 KameleonWrapper::modelScaleScaled() const { scale.z *= 6.371f; scale.w = 6; } - else if (units[0] == "m" && units[1] == "radian" & units[2] == "radian") { + else if (units[0] == "m" && units[1] == "radian" && units[2] == "radian") { // For spherical coordinate systems the radius is in meter scale.w = -log10(1.f / _max.x); } diff --git a/modules/multiresvolume/rendering/atlasmanager.cpp b/modules/multiresvolume/rendering/atlasmanager.cpp index e9d6c27f66..cdb5d856bf 100644 --- a/modules/multiresvolume/rendering/atlasmanager.cpp +++ b/modules/multiresvolume/rendering/atlasmanager.cpp @@ -113,7 +113,7 @@ void AtlasManager::updateAtlas(BufferIndex bufferIndex, std::vector& brickI _nDiskReads = 0; glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _pboHandle[bufferIndex]); - glBufferData(GL_PIXEL_UNPACK_BUFFER, _volumeSize, 0, GL_STREAM_DRAW); + glBufferData(GL_PIXEL_UNPACK_BUFFER, _volumeSize, nullptr, GL_STREAM_DRAW); float* mappedBuffer = reinterpret_cast(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); if (!mappedBuffer) { @@ -240,7 +240,7 @@ void AtlasManager::pboToAtlas(BufferIndex bufferIndex) { static_cast(dim[2]), // depth GL_RED, // format GL_FLOAT, // type - NULL // *pixels + nullptr // *pixels ); glBindTexture(GL_TEXTURE_3D, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); diff --git a/modules/multiresvolume/rendering/brickmanager.cpp b/modules/multiresvolume/rendering/brickmanager.cpp index 9eda8ba072..c3875329f0 100644 --- a/modules/multiresvolume/rendering/brickmanager.cpp +++ b/modules/multiresvolume/rendering/brickmanager.cpp @@ -98,9 +98,11 @@ bool BrickManager::readHeader() { numBricksFrame_ = numBricks_*numBricks_*numBricks_; // Calculate number of bricks in tree - unsigned int numOTLevels = static_cast(log((int)numBricks_) / log(2) + 1); + unsigned int numOTLevels = static_cast( + log(static_cast(numBricks_)) / log(2) + 1 + ); unsigned int numOTNodes = static_cast((pow(8, numOTLevels) - 1) / 7); - unsigned int numBSTNodes = static_cast(_header.numTimesteps_ * 2 - 1); + unsigned int numBSTNodes = _header.numTimesteps_ * 2 - 1; numBricksTree_ = numOTNodes * numBSTNodes; LDEBUG(fmt::format("Num OT levels: {}", numOTLevels)); LDEBUG(fmt::format("Num OT nodes: {}", numOTNodes)); @@ -325,9 +327,10 @@ bool BrickManager::DiskToPBO(BUFFER_INDEX _pboIndex) { // Map PBO glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboHandle_[_pboIndex]); - glBufferData(GL_PIXEL_UNPACK_BUFFER, volumeSize_, 0, GL_STREAM_DRAW); + glBufferData(GL_PIXEL_UNPACK_BUFFER, volumeSize_, nullptr, GL_STREAM_DRAW); float *mappedBuffer = reinterpret_cast( - glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); + glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY) + ); if (!mappedBuffer) { LERROR("Failed to map PBO"); @@ -463,7 +466,8 @@ bool BrickManager::PBOToAtlas(BUFFER_INDEX _pboIndex) { static_cast(dim[2]), // depth GL_RED, // format GL_FLOAT, // type - NULL); // *pixels + nullptr // *pixels + ); glBindTexture(GL_TEXTURE_3D, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); diff --git a/modules/multiresvolume/rendering/brickselection.h b/modules/multiresvolume/rendering/brickselection.h index 400a738295..e12c73269e 100644 --- a/modules/multiresvolume/rendering/brickselection.h +++ b/modules/multiresvolume/rendering/brickselection.h @@ -47,17 +47,17 @@ struct BrickSelection { BrickSelection() {} - BrickSelection(int numBricks, int numTimeSteps, SplitType splitType, - float splitPoints) + BrickSelection(int numBricks, int numTimeSteps, SplitType splitType_, + float splitPoints_) { - this->cover = BrickCover(numBricks); - this->lowT = 0; - this->highT = numTimeSteps; - this->brickIndex = 0; - this->splitType = splitType; - this->splitPoints = splitPoints; - this->nSpatialSplits = 0; - this->nTemporalSplits = 0; + cover = BrickCover(numBricks); + lowT = 0; + highT = numTimeSteps; + brickIndex = 0; + splitType = splitType_; + splitPoints = splitPoints_; + nSpatialSplits = 0; + nTemporalSplits = 0; } BrickSelection splitSpatially(bool x, bool y, bool z, unsigned int childBrickIndex, diff --git a/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp b/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp index 18bc979b0d..1480291584 100644 --- a/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp +++ b/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp @@ -131,10 +131,7 @@ void MultiresVolumeRaycaster::preRaycast(const RaycastData& data, "maxNumBricksPerAxis_" + id, static_cast(_tsp->header().xNumBricks_) ); - program.setUniform( - "paddedBrickDim_" + id, - static_cast(_tsp->paddedBrickDim()) - ); + program.setUniform("paddedBrickDim_" + id, _tsp->paddedBrickDim()); glm::size3_t size = _atlasManager->textureSize(); glm::ivec3 atlasSize(size.x, size.y, size.z); @@ -147,7 +144,7 @@ bool MultiresVolumeRaycaster::isCameraInside(const RenderData& data, // Camera rig position in world coordinates. glm::vec4 rigWorldPos = glm::vec4(data.camera.position().vec3(), 1.0); //rigWorldPos /= data.camera.scaling().x * pow(10.0, data.camera.scaling().y); - glm::mat4 invSgctMatrix = glm::inverse(data.camera.viewMatrix()); + //glm::mat4 invSgctMatrix = glm::inverse(data.camera.viewMatrix()); // Camera position in world coordinates. glm::vec4 camWorldPos = rigWorldPos; diff --git a/modules/multiresvolume/rendering/renderablemultiresvolume.cpp b/modules/multiresvolume/rendering/renderablemultiresvolume.cpp index 2b1c50632d..40e17034bc 100644 --- a/modules/multiresvolume/rendering/renderablemultiresvolume.cpp +++ b/modules/multiresvolume/rendering/renderablemultiresvolume.cpp @@ -163,29 +163,19 @@ namespace openspace { RenderableMultiresVolume::RenderableMultiresVolume (const ghoul::Dictionary& dictionary) : Renderable(dictionary) - , _transferFunction(nullptr) - , _timestep(0) - , _atlasMapSize(0) - , _tfBrickSelector(nullptr) - , _simpleTfBrickSelector(nullptr) - , _localTfBrickSelector(nullptr) - , _errorHistogramManager(nullptr) - , _histogramManager(nullptr) - , _localErrorHistogramManager(nullptr) - , _stepSizeCoefficient(StepSizeCoefficientInfo, 1.f, 0.01f, 10.f) + , _useGlobalTime(UseGlobalTimeInfo, false) + , _loop(LoopInfo, false) , _currentTime(CurrentTimeInfo, 0, 0, 0) , _memoryBudget(MemoryBudgetInfo, 0, 0, 0) , _streamingBudget(StreamingBudgetInfo, 0, 0, 0) - , _useGlobalTime(UseGlobalTimeInfo, false) - , _loop(LoopInfo, false) + , _stepSizeCoefficient(StepSizeCoefficientInfo, 1.f, 0.01f, 10.f) , _selectorName(SelectorNameInfo) - , _gatheringStats(false) , _statsToFile(StatsToFileInfo, false) , _statsToFileName(StatsToFileNameInfo) , _scalingExponent(ScalingExponentInfo, 1, -10, 20) - , _scaling(ScalingInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(10.f)) , _translation(TranslationInfo, glm::vec3(0.f), glm::vec3(0.f), glm::vec3(10.f)) , _rotation(RotationInfo, glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f), glm::vec3(6.28f)) + , _scaling(ScalingInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(10.f)) { std::string name; diff --git a/modules/multiresvolume/rendering/renderablemultiresvolume.h b/modules/multiresvolume/rendering/renderablemultiresvolume.h index f7ed684637..0a4f51571a 100644 --- a/modules/multiresvolume/rendering/renderablemultiresvolume.h +++ b/modules/multiresvolume/rendering/renderablemultiresvolume.h @@ -45,7 +45,7 @@ namespace ghoul::filesystem { class File; } namespace ghoul::opengl { class ProgramObject; class Texture; -} +} // namespace ghoul::opengl namespace openspace { @@ -102,7 +102,7 @@ private: // Stats timers std::string _statsFileName; - bool _gatheringStats; + bool _gatheringStats = false; std::chrono::system_clock::time_point _frameStart; std::chrono::duration _selectionDuration; std::chrono::duration _uploadDuration; @@ -110,7 +110,7 @@ private: unsigned int _nUsedBricks; unsigned int _nStreamedBricks; - int _timestep; + int _timestep = 0; std::string _filename; @@ -127,21 +127,21 @@ private: std::shared_ptr _tsp; std::vector _brickIndices; - int _atlasMapSize; + int _atlasMapSize = 0; std::shared_ptr _atlasManager; std::unique_ptr _raycaster; - TfBrickSelector* _tfBrickSelector; - SimpleTfBrickSelector* _simpleTfBrickSelector; - LocalTfBrickSelector* _localTfBrickSelector; + TfBrickSelector* _tfBrickSelector = nullptr; + SimpleTfBrickSelector* _simpleTfBrickSelector = nullptr; + LocalTfBrickSelector* _localTfBrickSelector = nullptr; Selector _selector; - HistogramManager* _histogramManager; - ErrorHistogramManager* _errorHistogramManager; - LocalErrorHistogramManager* _localErrorHistogramManager; + HistogramManager* _histogramManager = nullptr; + ErrorHistogramManager* _errorHistogramManager = nullptr; + LocalErrorHistogramManager* _localErrorHistogramManager = nullptr; float _w; PowerScaledCoordinate _pscOffset; diff --git a/modules/multiresvolume/rendering/shenbrickselector.h b/modules/multiresvolume/rendering/shenbrickselector.h index 24f4a736e0..14c4c4817a 100644 --- a/modules/multiresvolume/rendering/shenbrickselector.h +++ b/modules/multiresvolume/rendering/shenbrickselector.h @@ -39,7 +39,7 @@ public: ~ShenBrickSelector(); void setSpatialTolerance(float spatialTolerance); void setTemporalTolerance(float temporalTolerance); - void selectBricks(int timestep, std::vector& bricks); + void selectBricks(int timestep, std::vector& bricks) override; private: TSP* _tsp; diff --git a/modules/multiresvolume/rendering/simpletfbrickselector.h b/modules/multiresvolume/rendering/simpletfbrickselector.h index d93ddb2c84..52d9af79c4 100644 --- a/modules/multiresvolume/rendering/simpletfbrickselector.h +++ b/modules/multiresvolume/rendering/simpletfbrickselector.h @@ -43,14 +43,14 @@ public: int memoryBudget, int streamingBudget); ~SimpleTfBrickSelector(); - virtual bool initialize(); + bool initialize() override; - void selectBricks(int timestep, std::vector& bricks); + void selectBricks(int timestep, std::vector& bricks) override; void setMemoryBudget(int memoryBudget); void setStreamingBudget(int streamingBudget); bool calculateBrickImportances(); - private: +private: TSP* _tsp; HistogramManager* _histogramManager; TransferFunction* _transferFunction; diff --git a/modules/multiresvolume/rendering/tfbrickselector.h b/modules/multiresvolume/rendering/tfbrickselector.h index b0a8218baa..2a810c7c6b 100644 --- a/modules/multiresvolume/rendering/tfbrickselector.h +++ b/modules/multiresvolume/rendering/tfbrickselector.h @@ -44,9 +44,9 @@ public: ~TfBrickSelector(); - virtual bool initialize(); + bool initialize() override; - void selectBricks(int timestep, std::vector& bricks); + void selectBricks(int timestep, std::vector& bricks) override; void setMemoryBudget(int memoryBudget); void setStreamingBudget(int streamingBudget); bool calculateBrickErrors(); diff --git a/modules/server/include/connection.h b/modules/server/include/connection.h index e8137fdb62..1c86f69600 100644 --- a/modules/server/include/connection.h +++ b/modules/server/include/connection.h @@ -26,7 +26,7 @@ #define __OPENSPACE_MODULE_SERVER___CONNECTION___H__ #include -#include +#include #include #include #include diff --git a/modules/server/include/jsonconverters.h b/modules/server/include/jsonconverters.h index 5f04a5f781..84e855dd72 100644 --- a/modules/server/include/jsonconverters.h +++ b/modules/server/include/jsonconverters.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_SERVER___JSON_CONVERTERS___H__ #define __OPENSPACE_MODULE_SERVER___JSON_CONVERTERS___H__ -#include +#include #include namespace openspace::properties { diff --git a/modules/server/include/topics/topic.h b/modules/server/include/topics/topic.h index 94912b5918..536100ec30 100644 --- a/modules/server/include/topics/topic.h +++ b/modules/server/include/topics/topic.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_SERVER___TOPIC___H__ #define __OPENSPACE_MODULE_SERVER___TOPIC___H__ -#include +#include namespace openspace { diff --git a/modules/server/src/jsonconverters.cpp b/modules/server/src/jsonconverters.cpp index 2c2db24fcd..e02cae0966 100644 --- a/modules/server/src/jsonconverters.cpp +++ b/modules/server/src/jsonconverters.cpp @@ -24,11 +24,11 @@ #include +#include #include #include #include #include -#include using json = nlohmann::json; diff --git a/modules/server/src/topics/bouncetopic.cpp b/modules/server/src/topics/bouncetopic.cpp index 749b15166c..5720d3504d 100644 --- a/modules/server/src/topics/bouncetopic.cpp +++ b/modules/server/src/topics/bouncetopic.cpp @@ -30,7 +30,7 @@ namespace openspace { void BounceTopic::handleJson(const nlohmann::json& json) { _connection->sendJson(json); -}; +} bool BounceTopic::isDone() const { return false; diff --git a/modules/server/src/topics/luascripttopic.cpp b/modules/server/src/topics/luascripttopic.cpp index 8599ac03a6..809b0b2e1c 100644 --- a/modules/server/src/topics/luascripttopic.cpp +++ b/modules/server/src/topics/luascripttopic.cpp @@ -24,10 +24,10 @@ #include +#include #include #include #include -#include namespace { constexpr const char* ScriptKey = "script"; diff --git a/modules/server/src/topics/setpropertytopic.cpp b/modules/server/src/topics/setpropertytopic.cpp index 48b1c9a264..6e1a6bcdbc 100644 --- a/modules/server/src/topics/setpropertytopic.cpp +++ b/modules/server/src/topics/setpropertytopic.cpp @@ -24,12 +24,12 @@ #include +#include #include #include #include #include #include -#include namespace { constexpr const char* PropertyKey = "property"; @@ -70,6 +70,6 @@ void SetPropertyTopic::handleJson(const nlohmann::json& json) { bool SetPropertyTopic::isDone() const { return true; -}; +} } // namespace openspace diff --git a/modules/server/src/topics/topic.cpp b/modules/server/src/topics/topic.cpp index 4ff7893a38..d49212031e 100644 --- a/modules/server/src/topics/topic.cpp +++ b/modules/server/src/topics/topic.cpp @@ -22,16 +22,17 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include #include -#include + +#include +#include namespace openspace { void Topic::initialize(Connection* connection, size_t topicId) { _connection = connection; _topicId = topicId; -}; +} nlohmann::json Topic::wrappedPayload(const nlohmann::json& payload) const { // TODO: add message time @@ -40,7 +41,7 @@ nlohmann::json Topic::wrappedPayload(const nlohmann::json& payload) const { { "payload", payload } }; return j; -}; +} nlohmann::json Topic::wrappedError(std::string message, int code) { nlohmann::json j = { @@ -50,6 +51,6 @@ nlohmann::json Topic::wrappedError(std::string message, int code) { { "code", code } }; return j; -}; +} } // namespace openspace diff --git a/modules/server/src/topics/triggerpropertytopic.cpp b/modules/server/src/topics/triggerpropertytopic.cpp index bd10511afb..04fc148d65 100644 --- a/modules/server/src/topics/triggerpropertytopic.cpp +++ b/modules/server/src/topics/triggerpropertytopic.cpp @@ -24,10 +24,10 @@ #include +#include #include #include #include -#include namespace { constexpr const char* PropertyKey = "property"; @@ -62,6 +62,6 @@ void TriggerPropertyTopic::handleJson(const nlohmann::json& json) { bool TriggerPropertyTopic::isDone() const { return true; -}; +} } // namespace openspace diff --git a/modules/spacecraftinstruments/util/projectioncomponent.h b/modules/spacecraftinstruments/util/projectioncomponent.h index b920544568..3e47565c06 100644 --- a/modules/spacecraftinstruments/util/projectioncomponent.h +++ b/modules/spacecraftinstruments/util/projectioncomponent.h @@ -119,7 +119,7 @@ protected: SpiceManager::AberrationCorrection _aberration; std::vector _potentialTargets; float _fovy = -1.f; - float _aspectRatio = -1.f;; + float _aspectRatio = -1.f; GLuint _fboID = 0; GLuint _depthFboID = 0; diff --git a/modules/sync/torrentclient.cpp b/modules/sync/torrentclient.cpp index 1cb2e57f34..51ee1164c1 100644 --- a/modules/sync/torrentclient.cpp +++ b/modules/sync/torrentclient.cpp @@ -157,9 +157,10 @@ void TorrentClient::pollAlerts() { #endif // SYNC_USE_LIBTORRENT } -TorrentClient::TorrentId TorrentClient::addTorrentFile(const std::string& torrentFile, - const std::string& destination, - TorrentProgressCallback cb) +TorrentClient::TorrentId TorrentClient::addTorrentFile( + [[ maybe_unused ]] const std::string& torrentFile, + [[maybe_unused]] const std::string& destination, + [[maybe_unused]] TorrentProgressCallback cb) { #ifdef SYNC_USE_LIBTORRENT std::lock_guard guard(_mutex); @@ -189,9 +190,10 @@ TorrentClient::TorrentId TorrentClient::addTorrentFile(const std::string& torren #endif // SYNC_USE_LIBTORRENT } -TorrentClient::TorrentId TorrentClient::addMagnetLink(const std::string& magnetLink, - const std::string& destination, - TorrentProgressCallback cb) +TorrentClient::TorrentId TorrentClient::addMagnetLink( + [[maybe_unused]] const std::string& magnetLink, + [[maybe_unused]] const std::string& destination, + [[maybe_unused]] TorrentProgressCallback cb) { #ifdef SYNC_USE_LIBTORRENT std::lock_guard guard(_mutex); @@ -221,7 +223,7 @@ TorrentClient::TorrentId TorrentClient::addMagnetLink(const std::string& magnetL #endif // SYNC_USE_LIBTORRENT } -void TorrentClient::removeTorrent(TorrentId id) { +void TorrentClient::removeTorrent([[maybe_unused]] TorrentId id) { #ifdef SYNC_USE_LIBTORRENT std::lock_guard guard(_mutex); @@ -237,7 +239,7 @@ void TorrentClient::removeTorrent(TorrentId id) { #endif // SYNC_USE_LIBTORRENT } -void TorrentClient::notify(TorrentId id) { +void TorrentClient::notify([[maybe_unused]] TorrentId id) { #ifdef SYNC_USE_LIBTORRENT TorrentProgressCallback callback; TorrentProgress progress; diff --git a/modules/volume/CMakeLists.txt b/modules/volume/CMakeLists.txt index dbbbac1601..6c9d7c8bc5 100644 --- a/modules/volume/CMakeLists.txt +++ b/modules/volume/CMakeLists.txt @@ -35,6 +35,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/transferfunction.h ${CMAKE_CURRENT_SOURCE_DIR}/transferfunctionhandler.h ${CMAKE_CURRENT_SOURCE_DIR}/transferfunctionproperty.h + ${CMAKE_CURRENT_SOURCE_DIR}/rawvolumemetadata.h ${CMAKE_CURRENT_SOURCE_DIR}/lrucache.h ${CMAKE_CURRENT_SOURCE_DIR}/lrucache.inl ${CMAKE_CURRENT_SOURCE_DIR}/linearlrucache.h diff --git a/modules/volume/envelope.cpp b/modules/volume/envelope.cpp index e1aa17d6cd..6c75c61d1a 100644 --- a/modules/volume/envelope.cpp +++ b/modules/volume/envelope.cpp @@ -188,7 +188,7 @@ std::string EnvelopePoint::hexadecimalFromVec3(const glm::vec3& vec) const { json Envelope::jsonPoints() const { json j; - for (int i = 0; i < _points.size(); i++) { + for (size_t i = 0; i < _points.size(); i++) { j[i] = { { "color", diff --git a/modules/volume/envelope.h b/modules/volume/envelope.h index c727307570..7293bc4d5e 100644 --- a/modules/volume/envelope.h +++ b/modules/volume/envelope.h @@ -25,8 +25,8 @@ #ifndef __OPENSPACE_MODULE_VOLUME___ENVELOPE___H__ #define __OPENSPACE_MODULE_VOLUME___ENVELOPE___H__ +#include #include -#include struct lua_State; diff --git a/modules/volume/rawvolumemetadata.cpp b/modules/volume/rawvolumemetadata.cpp index aac4f07ef1..7f7eaf684c 100644 --- a/modules/volume/rawvolumemetadata.cpp +++ b/modules/volume/rawvolumemetadata.cpp @@ -29,19 +29,19 @@ #include namespace { - const constexpr char* KeyDimensions = "Dimensions"; - const constexpr char* KeyLowerDomainBound = "LowerDomainBound"; - const constexpr char* KeyUpperDomainBound = "UpperDomainBound"; + constexpr const char* KeyDimensions = "Dimensions"; + constexpr const char* KeyLowerDomainBound = "LowerDomainBound"; + constexpr const char* KeyUpperDomainBound = "UpperDomainBound"; - const constexpr char* KeyMinValue = "MinValue"; - const constexpr char* KeyMaxValue = "MaxValue"; + constexpr const char* KeyMinValue = "MinValue"; + constexpr const char* KeyMaxValue = "MaxValue"; - const constexpr char* KeyTime = "Time"; - const constexpr char* KeyDomainUnit = "DomainUnit"; - const constexpr char* KeyValueUnit = "ValueUnit"; + constexpr const char* KeyTime = "Time"; + constexpr const char* KeyDomainUnit = "DomainUnit"; + constexpr const char* KeyValueUnit = "ValueUnit"; - const constexpr char* KeyGridType = "GridType"; -} + constexpr const char* KeyGridType = "GridType"; +} // namespace namespace openspace::volume { @@ -104,8 +104,8 @@ ghoul::Dictionary RawVolumeMetadata::dictionary() { } if (hasValueRange) { - dict.setValue(KeyMinValue, static_cast(minValue)); - dict.setValue(KeyMaxValue, static_cast(maxValue)); + dict.setValue(KeyMinValue, minValue); + dict.setValue(KeyMaxValue, maxValue); } if (hasDomainUnit) { dict.setValue(KeyValueUnit, valueUnit); diff --git a/modules/volume/rendering/basicvolumeraycaster.cpp b/modules/volume/rendering/basicvolumeraycaster.cpp index b39346f967..ec4dada150 100644 --- a/modules/volume/rendering/basicvolumeraycaster.cpp +++ b/modules/volume/rendering/basicvolumeraycaster.cpp @@ -50,9 +50,9 @@ BasicVolumeRaycaster::BasicVolumeRaycaster( std::shared_ptr volumeTexture, std::shared_ptr transferFunction, std::shared_ptr clipPlanes) - : _volumeTexture(volumeTexture) + : _clipPlanes(clipPlanes) + , _volumeTexture(volumeTexture) , _transferFunction(transferFunction) - , _clipPlanes(clipPlanes) , _boundingBox(glm::vec3(1.0)) {} diff --git a/modules/volume/rendering/renderabletimevaryingvolume.cpp b/modules/volume/rendering/renderabletimevaryingvolume.cpp index 3c1e43047f..ec0991e6e7 100644 --- a/modules/volume/rendering/renderabletimevaryingvolume.cpp +++ b/modules/volume/rendering/renderabletimevaryingvolume.cpp @@ -321,7 +321,7 @@ void RenderableTimeVaryingVolume::initializeGL() { } t.histogram = std::make_shared(0.f, 1.f, 100); - for (int i = 0; i < t.rawVolume->nCells(); ++i) { + for (size_t i = 0; i < t.rawVolume->nCells(); ++i) { t.histogram->add(data[i]); } diff --git a/modules/volume/transferfunctionproperty.cpp b/modules/volume/transferfunctionproperty.cpp index 266a10dfd4..c78ca045aa 100644 --- a/modules/volume/transferfunctionproperty.cpp +++ b/modules/volume/transferfunctionproperty.cpp @@ -26,41 +26,41 @@ #include +namespace { + +openspace::volume::TransferFunction fromLuaConversion(lua_State* state, bool& success) { + openspace::volume::TransferFunction tf; + success = tf.setEnvelopesFromLua(state); + return tf; +} + +bool toLuaConversion(lua_State* state, openspace::volume::TransferFunction value) { + return value.envelopesToLua(state); +} + +openspace::volume::TransferFunction fromStringConversion(std::string val, bool& success) { + openspace::volume::TransferFunction tf; + success = tf.setEnvelopesFromString(val); + return tf; +} + +bool toStringConversion(std::string& outValue, + openspace::volume::TransferFunction inValue) +{ + outValue = inValue.serializedToString(); + return true; +} + +} // namespace + namespace openspace::properties { -#define DEFAULT_FROM_LUA_LAMBDA(TYPE, DEFAULT_VALUE) \ - [](lua_State* state, bool& success) -> TYPE { \ - TYPE TF; \ - success = true; \ - TF.setEnvelopesFromLua(state); \ - return TF; \ - } - -#define DEFAULT_TO_LUA_LAMBDA(TYPE) \ - [](lua_State* state, TYPE value) -> bool { \ - bool success = value.envelopesToLua(state); \ - return success; \ - } - -#define DEFAULT_FROM_STRING_LAMBDA(TYPE, DEFAULT_VALUE) \ - [](std::string val, bool& success) -> TYPE { \ - TYPE TF; \ - success = TF.setEnvelopesFromString(val); \ - return TF; \ - } - -#define DEFAULT_TO_STRING_LAMBDA(TYPE) \ - [](std::string& outValue, TYPE inValue) -> bool { \ - outValue = inValue.serializedToString(); \ - return true; \ - } - REGISTER_TEMPLATEPROPERTY_SOURCE(TransferFunctionProperty, volume::TransferFunction, volume::TransferFunction(), - DEFAULT_FROM_LUA_LAMBDA(volume::TransferFunction, volume::TransferFunction()), - DEFAULT_TO_LUA_LAMBDA(volume::TransferFunction), - DEFAULT_FROM_STRING_LAMBDA(volume::TransferFunction, volume::TransferFunction()), - DEFAULT_TO_STRING_LAMBDA(volume::TransferFunction), + fromLuaConversion, + toLuaConversion, + fromStringConversion, + toStringConversion, LUA_TTABLE); } // namespace openspace::properties diff --git a/src/engine/openspaceengine_lua.inl b/src/engine/openspaceengine_lua.inl index 9b7878aa91..f0858aefff 100644 --- a/src/engine/openspaceengine_lua.inl +++ b/src/engine/openspaceengine_lua.inl @@ -73,7 +73,11 @@ int addVirtualProperty(lua_State* L) { if (type == "BoolProperty") { const bool v = ghoul::lua::value(L, 5); prop = std::make_unique( - properties::Property::PropertyInfo{ identifier.c_str(), name.c_str(), description.c_str() }, + properties::Property::PropertyInfo { + identifier.c_str(), + name.c_str(), + description.c_str() + }, v ); } @@ -83,7 +87,11 @@ int addVirtualProperty(lua_State* L) { const int max = ghoul::lua::value(L, 7); prop = std::make_unique( - properties::Property::PropertyInfo{ identifier.c_str(), name.c_str(), description.c_str() }, + properties::Property::PropertyInfo { + identifier.c_str(), + name.c_str(), + description.c_str() + }, v, min, max @@ -95,7 +103,11 @@ int addVirtualProperty(lua_State* L) { const float max = ghoul::lua::value(L, 7); prop = std::make_unique( - properties::Property::PropertyInfo{ identifier.c_str(), name.c_str(), description.c_str() }, + properties::Property::PropertyInfo { + identifier.c_str(), + name.c_str(), + description.c_str() + }, v, min, max @@ -103,7 +115,11 @@ int addVirtualProperty(lua_State* L) { } else if (type == "TriggerProperty") { prop = std::make_unique( - properties::Property::PropertyInfo{ identifier.c_str(), name.c_str(), description.c_str() } + properties::Property::PropertyInfo { + identifier.c_str(), + name.c_str(), + description.c_str() + } ); } else { diff --git a/src/rendering/luaconsole.cpp b/src/rendering/luaconsole.cpp index 2668d9eb24..ab605b9742 100644 --- a/src/rendering/luaconsole.cpp +++ b/src/rendering/luaconsole.cpp @@ -411,7 +411,7 @@ bool LuaConsole::keyboardCallback(Key key, KeyModifier modifier, KeyAction actio ); return true; } - + // Go to the previous '.' character if (modifierControl && key == Key::Left) { std::string current = _commands.at(_activeCommand);