diff --git a/.gitignore b/.gitignore index be6fe0e..57f5a3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .vscode -build +build* docs external resource/*/.* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 14adae7..cd01ffe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ option(USE_FMA "Use fma" 1) option(LOG_DEBUG "Show debug logs" 0) option(LOG_TRACE "Show trace logs" 0) option(NATIVE "Build with -march=native" 0) +option(GL_OLD "Use OpenGL 4.2 not 4.6" 0) find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) @@ -23,10 +24,15 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -set(CMAKE_CXX_FLAGS "-Wall -Wextra") +if(WIN32) + add_definitions(/std:c++latest) + add_compile_definitions(WIN32_LEAN_AND_MEAN=) +else() + set(CMAKE_CXX_FLAGS "-Wall -Wextra") +endif() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_compile_definitions(FIXED_WINDOW=${FIXED_WINDOW} LOG_DEBUG=${LOG_DEBUG} LOG_TRACE=${LOG_TRACE} HN_USE_FILESYSTEM=1) +add_compile_definitions(FIXED_WINDOW=${FIXED_WINDOW} LOG_DEBUG=${LOG_DEBUG} LOG_TRACE=${LOG_TRACE} GL_OLD=${GL_OLD} HN_USE_FILESYSTEM=1) if(PROFILING) add_compile_definitions(TRACY_ENABLE=1) endif(PROFILING) @@ -38,23 +44,39 @@ if(SIMD_LEVEL EQUAL "avx2") elseif(SIMD_LEVEL EQUAL "avx512f") add_compile_definitions(FN_COMPILE_AVX512=1) endif() -add_definitions(-m${SIMD_LEVEL}) +if(WIN32) + add_definitions(/arch:AVX2) +else() + add_definitions(-m${SIMD_LEVEL}) +endif() if(USE_FMA) - add_definitions(-mfma) + if(WIN32) + add_definitions(/GL /fp:fast) + else() + add_definitions(-mfma) + endif() endif(USE_FMA) file(GLOB_RECURSE CORE_SOURCES "src/core/*.cpp" "deps/tracy/TracyClient.cpp") set(CORE_HEADERS "deps/toml++" "deps/robin_hood" "deps/libguarded" "deps/tracy") -set(CORE_LIBS pthread dl glm::glm_static picoquic-core zstd::zstd_static) +set(CORE_LIBS glm::glm_static zstd::zstd_static) # picoquic file(GLOB_RECURSE CLIENT_SOURCES "src/client/*.cpp" "deps/imgui/*.cpp" "deps/meshoptimizer/*.cpp" "deps/gl3w/gl3w.c" "deps/volk/volk.c") -set(CLIENT_HEADERS "deps/imgui" "deps/meshoptimizer" "deps/gl3w" "deps/volk") +set(CLIENT_HEADERS "deps/imgui" "deps/meshoptimizer" "deps/gl3w" "deps/volk") # vulkan set(CLIENT_LIBS glfw) file(GLOB_RECURSE SERVER_SOURCES "src/server/*.cpp" "deps/FastNoiseSIMD/*.cpp") set(SERVER_HEADERS "deps/FastNoiseSIMD") set(SERVER_LINKED) +if(WIN32) + set(CLIENT_HEADERS ${CLIENT_HEADERS} $ENV{VULKAN_SDK}\\include) + find_package(OpenSSL) + set(CORE_LIBS ${CORE_LIBS} ${CMAKE_BINARY_DIR}/libs/*.lib ${OPENSSL_LIBRARIES} ws2_32) +else() + set(CORE_LIBS ${CORE_LIBS} picoquic-core pthread dl) +endif() + # All in one exec add_executable(univerxel "src/main.cpp" ${CORE_SOURCES} ${CLIENT_SOURCES} ${SERVER_SOURCES}) target_compile_features(univerxel PUBLIC cxx_std_17) diff --git a/README.md b/README.md index 295b601..623067e 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Work in progress galaxy down to atom (mostly centimeter) online voxel game - [Optionally](#optionally) - [Installation](#installation) - [Additionally](#additionally) + - [Windows](#windows) - [RoadMap](#roadmap) - [License](#license) - [Contact](#contact) @@ -102,6 +103,7 @@ PROFILING | Tracy profiling | `0` LOG_DEBUG | Debug logs | `0` LOG_TRACE | Trace logs | `0` NATIVE | Optimize for native CPU | `0` +GL_OLD | Use OpenGL 4.2 not 4.6 | `0` 1. Build Make ```sh @@ -132,6 +134,14 @@ cd resource/textures-src ./merge.py # Combine grey images to RGB ``` +### Windows + +1. Setup Visual Studio 2017+ with C++ +2. Clone picotls and picoquic in a separate directory +3. Set `%OPENSSL64DIR%` and check `WindowsPort.md` and `Readme.md` note from respective library +4. Compile then with MsBuild +5. Copy picoquic.lib, picotls-openssl.lib and picotls-fusion.lib to build/libs +6. Build univerxel with cmake `-A x64` and MsBuild ## RoadMap diff --git a/TODO.md b/TODO.md index 1d36f0e..ca2709a 100644 --- a/TODO.md +++ b/TODO.md @@ -46,6 +46,9 @@ - [~] CI build - [ ] CMake package - [x] GitLab CI + - Platfoms + - [x] Linux + - [~] Windows - [ ] Universe - [ ] Galaxy - [ ] Rotation @@ -107,5 +110,6 @@ - [ ] Deferred - [ ] Cascaded shadow maps - [ ] Ray Tracing + - [ ] Avoid transparent back-face - [ ] Translucency - Back face Depth based diff --git a/deps/picoquic/CMakeLists.txt b/deps/picoquic/CMakeLists.txt index 0dc7cf8..76ad0f9 100644 --- a/deps/picoquic/CMakeLists.txt +++ b/deps/picoquic/CMakeLists.txt @@ -4,45 +4,6 @@ cmake_policy(SET CMP0048 NEW) project(picoquic VERSION 0.0.1 LANGUAGES C CXX) find_package (Threads REQUIRED) -add_subdirectory("picotls") - -set(CMAKE_C_STANDARD 11) - -set(CMAKE_C_FLAGS "-std=c99 -Wall -Werror -O2 -g ${CC_WARNING_FLAGS} ${CMAKE_C_FLAGS}") - -if(DISABLE_DEBUG_PRINTF) - set(CMAKE_C_FLAGS "-DDISABLE_DEBUG_PRINTF ${CMAKE_C_FLAGS}") -endif() - -set(PICOQUIC_LIBRARY_FILES - bbr.c - bytestream.c - cc_common.c - cubic.c - fastcc.c - frames.c - intformat.c - logger.c - logwriter.c - newreno.c - packet.c - picohash.c - picosocks.c - picosplay.c - quicctx.c - sacks.c - sender.c - sim_link.c - sockloop.c - spinbit.c - ticket_store.c - token_store.c - tls_api.c - transport.c - unified_log.c - util.c -) - set(PICOQUIC_CORE_HEADERS picoquic.h picosocks.h @@ -56,5 +17,50 @@ set(PICOQUIC_CORE_HEADERS find_package(OpenSSL) include_directories(picoquic "picotls/include" ${OPENSSL_INCLUDE_DIR}) -add_library(picoquic-core ${PICOQUIC_CORE_HEADERS} ${PICOQUIC_LIBRARY_FILES}) -target_link_libraries(picoquic-core picotls-core picotls-openssl picotls-fusion ${OPENSSL_LIBRARIES}) + +if(WIN32) + MESSAGE(WARNING "Please build picoquic and picotls with Visual Studio") + MESSAGE(WARNING "Then put picoquic.lib, picotls-core.lib picotls-openssl.lib and picotls-fusion.lib in build/libs") +else() + add_subdirectory("picotls") + + set(CMAKE_C_STANDARD 11) + + set(CMAKE_C_FLAGS "-std=c99 -O2 -g ${CC_WARNING_FLAGS} ${CMAKE_C_FLAGS}") + + if(DISABLE_DEBUG_PRINTF) + set(CMAKE_C_FLAGS "-DDISABLE_DEBUG_PRINTF ${CMAKE_C_FLAGS}") + endif() + + set(PICOQUIC_LIBRARY_FILES + bbr.c + bytestream.c + cc_common.c + cubic.c + fastcc.c + frames.c + intformat.c + logger.c + logwriter.c + newreno.c + packet.c + picohash.c + picosocks.c + picosplay.c + quicctx.c + sacks.c + sender.c + sim_link.c + sockloop.c + spinbit.c + ticket_store.c + token_store.c + tls_api.c + transport.c + unified_log.c + util.c + ) + + add_library(picoquic-core ${PICOQUIC_CORE_HEADERS} ${PICOQUIC_LIBRARY_FILES}) + target_link_libraries(picoquic-core picotls-core picotls-openssl picotls-fusion ${OPENSSL_LIBRARIES}) +endif() diff --git a/deps/toml++/LICENSE b/deps/toml++/LICENSE new file mode 100644 index 0000000..66462ce --- /dev/null +++ b/deps/toml++/LICENSE @@ -0,0 +1,16 @@ +MIT License + +Copyright (c) 2019-2020 Mark Gillard + +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. diff --git a/deps/toml++/toml.h b/deps/toml++/toml.h index fde10be..3586067 100644 --- a/deps/toml++/toml.h +++ b/deps/toml++/toml.h @@ -1,19 +1,20 @@ -#define TOML_HEADER_ONLY 0 //# This file is a part of toml++ and is subject to the the terms of the MIT license. //# Copyright (c) 2019-2020 Mark Gillard //# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. // SPDX-License-Identifier: MIT -//# {{ #ifndef INCLUDE_TOMLPLUSPLUS_H #define INCLUDE_TOMLPLUSPLUS_H -//# }} //# Note: most of these would be included transitively but //# they're listed explicitly here because this file //# is used as the source for generate_single_header.py. #include "toml_preprocessor.h" + +TOML_PUSH_WARNINGS +TOML_DISABLE_SPAM_WARNINGS + #include "toml_common.h" #include "toml_date_time.h" #include "toml_print_to_stream.h" @@ -22,7 +23,6 @@ #include "toml_array.h" #include "toml_table.h" #include "toml_node_view.h" -#include "toml_utf8_generated.h" #include "toml_utf8.h" #include "toml_formatter.h" #include "toml_default_formatter.h" @@ -40,68 +40,109 @@ #include "toml_default_formatter.hpp" #include "toml_json_formatter.hpp" #if TOML_PARSER + #include "toml_utf8_streams.hpp" #include "toml_parser.hpp" #endif // TOML_PARSER -#if !TOML_ALL_INLINE +#if !TOML_HEADER_ONLY #include "toml_instantiations.hpp" -#endif // !TOML_ALL_INLINE +#endif // !TOML_HEADER_ONLY #endif // TOML_IMPLEMENTATION +TOML_POP_WARNINGS // TOML_DISABLE_SPAM_WARNINGS + // macro hygiene #if TOML_UNDEF_MACROS - #undef TOML_INT_CHARCONV - #undef TOML_FLOAT_CHARCONV + #undef TOML_ABI_NAMESPACES + #undef TOML_ABI_NAMESPACE_BOOL + #undef TOML_ABI_NAMESPACE_END + #undef TOML_ABI_NAMESPACE_START + #undef TOML_ALWAYS_INLINE + #undef TOML_ANON_NAMESPACE + #undef TOML_ANON_NAMESPACE_END + #undef TOML_ANON_NAMESPACE_START + #undef TOML_ARM + #undef TOML_ASSERT + #undef TOML_ASSUME + #undef TOML_ASYMMETRICAL_EQUALITY_OPS #undef TOML_ATTR - #undef TOML_PUSH_WARNINGS - #undef TOML_DISABLE_SWITCH_WARNINGS + #undef TOML_CLANG + #undef TOML_COMPILER_EXCEPTIONS + #undef TOML_CONCAT + #undef TOML_CONCAT_1 + #undef TOML_CONSTEVAL + #undef TOML_CPP + #undef TOML_DISABLE_ARITHMETIC_WARNINGS #undef TOML_DISABLE_INIT_WARNINGS - #undef TOML_DISABLE_VTABLE_WARNINGS - #undef TOML_DISABLE_PADDING_WARNINGS - #undef TOML_DISABLE_FLOAT_WARNINGS + #undef TOML_DISABLE_SPAM_WARNINGS #undef TOML_DISABLE_SHADOW_WARNINGS #undef TOML_DISABLE_SUGGEST_WARNINGS - #undef TOML_DISABLE_ALL_WARNINGS - #undef TOML_POP_WARNINGS - #undef TOML_ALWAYS_INLINE - #undef TOML_NEVER_INLINE - #undef TOML_ASSUME - #undef TOML_UNREACHABLE - #undef TOML_INTERFACE + #undef TOML_DISABLE_SWITCH_WARNINGS + #undef TOML_DISABLE_WARNINGS + #undef TOML_ENABLE_WARNINGS #undef TOML_EMPTY_BASES - #undef TOML_CPP_VERSION - #undef TOML_CPP - #undef TOML_MAY_THROW - #undef TOML_NO_DEFAULT_CASE - #undef TOML_CONSTEVAL - #undef TOML_LIKELY - #undef TOML_UNLIKELY - #undef TOML_NODISCARD_CTOR - #undef TOML_MAKE_VERSION + #undef TOML_EVAL_BOOL_0 + #undef TOML_EVAL_BOOL_1 + #undef TOML_EXTERNAL_LINKAGE + #undef TOML_FLOAT128 + #undef TOML_FLOAT16 + #undef TOML_FLOAT_CHARCONV + #undef TOML_FP16 + #undef TOML_GCC + #undef TOML_HAS_ATTR + #undef TOML_HAS_CUSTOM_OPTIONAL_TYPE + #undef TOML_HAS_INCLUDE + #undef TOML_ICC + #undef TOML_ICC_CL + #undef TOML_IMPLEMENTATION + #undef TOML_IMPL_NAMESPACE_END + #undef TOML_IMPL_NAMESPACE_START + #undef TOML_INT128 + #undef TOML_INTELLISENSE + #undef TOML_INTERFACE + #undef TOML_INTERNAL_LINKAGE + #undef TOML_INT_CHARCONV + #undef TOML_LANG_AT_LEAST #undef TOML_LANG_EFFECTIVE_VERSION #undef TOML_LANG_HIGHER_THAN - #undef TOML_LANG_AT_LEAST #undef TOML_LANG_UNRELEASED - #undef TOML_STRING_PREFIX_1 - #undef TOML_STRING_PREFIX - #undef TOML_UNDEF_MACROS - #undef TOML_RELOPS_REORDERING - #undef TOML_ASYMMETRICAL_EQUALITY_OPS - #undef TOML_ALL_INLINE - #undef TOML_IMPLEMENTATION - #undef TOML_EXTERNAL_LINKAGE - #undef TOML_INTERNAL_LINKAGE - #undef TOML_INTERNAL_NAMESPACE - #undef TOML_COMPILER_EXCEPTIONS - #undef TOML_TRIVIAL_ABI - #undef TOML_ABI_NAMESPACES - #undef TOML_ABI_NAMESPACE_START - #undef TOML_ABI_NAMESPACE_END - #undef TOML_PARSER_TYPENAME #undef TOML_LAUNDER + #undef TOML_LIFETIME_HOOKS + #undef TOML_LIKELY + #undef TOML_MAKE_BITOPS + #undef TOML_MAKE_VERSION + #undef TOML_MAY_THROW + #undef TOML_MSVC + #undef TOML_NAMESPACE + #undef TOML_NAMESPACE_END + #undef TOML_NAMESPACE_START + #undef TOML_NEVER_INLINE + #undef TOML_NODISCARD_CTOR + #undef TOML_NO_DEFAULT_CASE + #undef TOML_PARSER_TYPENAME + #undef TOML_POP_WARNINGS + #undef TOML_PUSH_WARNINGS + #undef TOML_SA_LIST_BEG + #undef TOML_SA_LIST_END + #undef TOML_SA_LIST_NEW + #undef TOML_SA_LIST_NXT + #undef TOML_SA_LIST_SEP + #undef TOML_SA_NATIVE_VALUE_TYPE_LIST + #undef TOML_SA_NEWLINE + #undef TOML_SA_NODE_TYPE_LIST + #undef TOML_SA_UNWRAPPED_NODE_TYPE_LIST + #undef TOML_SA_VALUE_EXACT_FUNC_MESSAGE + #undef TOML_SA_VALUE_FUNC_MESSAGE + #undef TOML_SA_VALUE_MESSAGE_CONST_CHAR8 + #undef TOML_SA_VALUE_MESSAGE_U8STRING_VIEW + #undef TOML_SA_VALUE_MESSAGE_WSTRING + #undef TOML_SIMPLE_STATIC_ASSERT_MESSAGES + #undef TOML_TRIVIAL_ABI + #undef TOML_UINT128 + #undef TOML_UNLIKELY + #undef TOML_UNREACHABLE + #undef TOML_USING_ANON_NAMESPACE #endif -//# {{ #endif // INCLUDE_TOMLPLUSPLUS_H -//# }} diff --git a/deps/toml++/toml_array.h b/deps/toml++/toml_array.h index 48c7f8d..36fafb7 100644 --- a/deps/toml++/toml_array.h +++ b/deps/toml++/toml_array.h @@ -6,21 +6,18 @@ #pragma once #include "toml_value.h" -TOML_PUSH_WARNINGS -TOML_DISABLE_VTABLE_WARNINGS - -namespace toml::impl +TOML_IMPL_NAMESPACE_START { template class TOML_TRIVIAL_ABI array_iterator final { private: - friend class ::toml::array; + friend class TOML_NAMESPACE::array; using raw_mutable_iterator = std::vector>::iterator; using raw_const_iterator = std::vector>::const_iterator; using raw_iterator = std::conditional_t; - + mutable raw_iterator raw_; array_iterator(raw_mutable_iterator raw) noexcept @@ -95,61 +92,61 @@ namespace toml::impl } [[nodiscard]] - friend constexpr array_iterator operator + (const array_iterator& lhs, ptrdiff_t rhs) noexcept + friend array_iterator operator + (const array_iterator& lhs, ptrdiff_t rhs) noexcept { return { lhs.raw_ + rhs }; } [[nodiscard]] - friend constexpr array_iterator operator + (ptrdiff_t lhs, const array_iterator& rhs) noexcept + friend array_iterator operator + (ptrdiff_t lhs, const array_iterator& rhs) noexcept { return { rhs.raw_ + lhs }; } [[nodiscard]] - friend constexpr array_iterator operator - (const array_iterator& lhs, ptrdiff_t rhs) noexcept + friend array_iterator operator - (const array_iterator& lhs, ptrdiff_t rhs) noexcept { return { lhs.raw_ - rhs }; } [[nodiscard]] - friend constexpr ptrdiff_t operator - (const array_iterator& lhs, const array_iterator& rhs) noexcept + friend ptrdiff_t operator - (const array_iterator& lhs, const array_iterator& rhs) noexcept { return lhs.raw_ - rhs.raw_; } [[nodiscard]] - friend constexpr bool operator == (const array_iterator& lhs, const array_iterator& rhs) noexcept + friend bool operator == (const array_iterator& lhs, const array_iterator& rhs) noexcept { return lhs.raw_ == rhs.raw_; } [[nodiscard]] - friend constexpr bool operator != (const array_iterator& lhs, const array_iterator& rhs) noexcept + friend bool operator != (const array_iterator& lhs, const array_iterator& rhs) noexcept { return lhs.raw_ != rhs.raw_; } [[nodiscard]] - friend constexpr bool operator < (const array_iterator& lhs, const array_iterator& rhs) noexcept + friend bool operator < (const array_iterator& lhs, const array_iterator& rhs) noexcept { return lhs.raw_ < rhs.raw_; } [[nodiscard]] - friend constexpr bool operator <= (const array_iterator& lhs, const array_iterator& rhs) noexcept + friend bool operator <= (const array_iterator& lhs, const array_iterator& rhs) noexcept { return lhs.raw_ <= rhs.raw_; } [[nodiscard]] - friend constexpr bool operator > (const array_iterator& lhs, const array_iterator& rhs) noexcept + friend bool operator > (const array_iterator& lhs, const array_iterator& rhs) noexcept { return lhs.raw_ > rhs.raw_; } [[nodiscard]] - friend constexpr bool operator >= (const array_iterator& lhs, const array_iterator& rhs) noexcept + friend bool operator >= (const array_iterator& lhs, const array_iterator& rhs) noexcept { return lhs.raw_ >= rhs.raw_; } @@ -160,110 +157,169 @@ namespace toml::impl return *(raw_ + idx)->get(); } + TOML_DISABLE_WARNINGS + template > - [[nodiscard]] operator array_iterator() const noexcept { return array_iterator{ raw_ }; } + + TOML_ENABLE_WARNINGS }; template [[nodiscard]] TOML_ATTR(returns_nonnull) - TOML_ALWAYS_INLINE - auto* make_node(T&& val) noexcept + auto* make_node_specialized(T&& val) noexcept { - using type = unwrapped>; + using type = unwrap_node>; + static_assert(!std::is_same_v); + static_assert(!is_node_view); + if constexpr (is_one_of) { - static_assert( - std::is_rvalue_reference_v, - "Tables and arrays may only be moved (not copied)." - ); return new type{ std::forward(val) }; } else { static_assert( - is_value_or_promotable, + !is_wide_string || TOML_WINDOWS_COMPAT, + "Instantiating values from wide-character strings is only " + "supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); + static_assert( + is_native || is_losslessly_convertible_to_native, "Value initializers must be (or be promotable to) one of the TOML value types" ); - return new value{ std::forward(val) }; + if constexpr (is_wide_string) + { + #if TOML_WINDOWS_COMPAT + return new value{ narrow(std::forward(val)) }; + #else + static_assert(dependent_false, "Evaluated unreachable branch!"); + #endif + } + else + return new value{ std::forward(val) }; } } template [[nodiscard]] - TOML_ATTR(returns_nonnull) - TOML_ALWAYS_INLINE + auto* make_node(T&& val) noexcept + { + using type = unwrap_node>; + if constexpr (std::is_same_v || is_node_view) + { + if constexpr (is_node_view) + { + if (!val) + return static_cast(nullptr); + } + + return std::forward(val).visit([](auto&& concrete) noexcept + { + return static_cast(make_node_specialized(std::forward(concrete))); + }); + } + else + return make_node_specialized(std::forward(val)); + } + + template + [[nodiscard]] auto* make_node(inserter&& val) noexcept { return make_node(std::move(val.value)); } } +TOML_IMPL_NAMESPACE_END -namespace toml +TOML_NAMESPACE_START { - [[nodiscard]] TOML_API bool operator == (const array& lhs, const array& rhs) noexcept; - [[nodiscard]] TOML_API bool operator != (const array& lhs, const array& rhs) noexcept; - template - std::basic_ostream& operator << (std::basic_ostream&, const array&); - - /// \brief A RandomAccessIterator for iterating over nodes in a toml::array. + /// \brief A RandomAccessIterator for iterating over elements in a toml::array. using array_iterator = impl::array_iterator; - /// \brief A RandomAccessIterator for iterating over const nodes in a toml::array. + /// \brief A RandomAccessIterator for iterating over const elements in a toml::array. using const_array_iterator = impl::array_iterator; /// \brief A TOML array. /// /// \detail The interface of this type is modeled after std::vector, with some /// additional considerations made for the heterogeneous nature of a - /// TOML array. \cpp + /// TOML array. /// - /// auto tbl = toml::parse("arr = [1, 2, 3, 4, 'five']"sv); - /// auto& arr = *tbl.get_as("arr"); - /// std::cout << arr << std::endl; + /// \godbolt{sjK4da} /// - /// for (size_t i = 0; i < arr.size(); i++) + /// \cpp + /// + /// toml::table tbl = toml::parse(R"( + /// arr = [1, 2, 3, 4, 'five'] + /// )"sv); + /// + /// // get the element as an array + /// toml::array& arr = *tbl.get_as("arr"); + /// std::cout << arr << "\n"; + /// + /// // increment each element with visit() + /// for (auto&& elem : arr) /// { - /// arr[i].visit([](auto&& el) noexcept - /// { - /// if constexpr (toml::is_number) - /// (*el)++; - /// else if constexpr (toml::is_string) - /// el = "six"sv; - /// }); + /// elem.visit([](auto&& el) noexcept + /// { + /// if constexpr (toml::is_number) + /// (*el)++; + /// else if constexpr (toml::is_string) + /// el = "six"sv; + /// }); /// } - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// + /// // add and remove elements /// arr.push_back(7); /// arr.push_back(8.0f); /// arr.push_back("nine"sv); /// arr.erase(arr.cbegin()); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// - /// arr.emplace_back(10, 11.0); - /// std::cout << arr << std::endl; + /// // emplace elements + /// arr.emplace_back("ten"); + /// arr.emplace_back(11, 12.0); + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, 2, 3, 4, "five"] - /// [2, 3, 4, 5, "six"] - /// [3, 4, 5, "six", 7, 8.0, "nine"] - /// [3, 4, 5, "six", 7, 8.0, "nine", [10, 11.0]] + /// [ 1, 2, 3, 4, 'five' ] + /// [ 2, 3, 4, 5, 'six' ] + /// [ 3, 4, 5, 'six', 7, 8.0, 'nine' ] + /// [ 3, 4, 5, 'six', 7, 8.0, 'nine', 'ten', [ 11, 12.0 ] ] /// \eout class TOML_API array final : public node { private: friend class TOML_PARSER_TYPENAME; - std::vector> values; + std::vector> elements; void preinsertion_resize(size_t idx, size_t count) noexcept; + template + void emplace_back_if_not_empty_view(T&& val) noexcept + { + if constexpr (impl::is_node_view) + { + if (!val) + return; + } + elements.emplace_back(impl::make_node(std::forward(val))); + } + + #if TOML_LIFETIME_HOOKS + void lh_ctor() noexcept; + void lh_dtor() noexcept; + #endif + public: using value_type = node; @@ -272,29 +328,42 @@ namespace toml using reference = node&; using const_reference = const node&; - /// \brief A RandomAccessIterator for iterating over nodes in a toml::array. + /// \brief A RandomAccessIterator for iterating over elements in a toml::array. using iterator = array_iterator; - /// \brief A RandomAccessIterator for iterating over const nodes in a toml::array. + /// \brief A RandomAccessIterator for iterating over const elements in a toml::array. using const_iterator = const_array_iterator; /// \brief Default constructor. TOML_NODISCARD_CTOR array() noexcept; + /// \brief Copy constructor. + TOML_NODISCARD_CTOR + array(const array&) noexcept; + /// \brief Move constructor. TOML_NODISCARD_CTOR array(array&& other) noexcept; - /// \brief Constructs an array with one or more initial values. + /// \brief Copy-assignment operator. + array& operator= (const array&) noexcept; + + /// \brief Move-assignment operator. + array& operator= (array&& rhs) noexcept; + + /// \brief Destructor. + ~array() noexcept override; + + /// \brief Constructs an array with one or more initial elements. /// /// \detail \cpp /// auto arr = toml::array{ 1, 2.0, "three"sv, toml::array{ 4, 5 } }; - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, 2.0, "three", [4, 5]] + /// [ 1, 2.0, 'three', [ 4, 5 ] ] /// \eout /// /// \remark \parblock If you need to construct an array with one child array element, the array's move constructor @@ -303,8 +372,8 @@ namespace toml /// // desired result: [ [ 42 ] ] /// auto bad = toml::array{ toml::array{ 42 } } /// auto good = toml::array{ toml::inserter{ toml::array{ 42 } } } - /// std::cout << "bad: " << bad << std::endl; - /// std::cout << "good:" << good << std::endl; + /// std::cout << "bad: " << bad << "\n"; + /// std::cout << "good:" << good << "\n"; /// \ecpp /// /// \out @@ -314,166 +383,140 @@ namespace toml /// /// \endparblock /// - /// \tparam U One of the TOML node or value types (or a type promotable to one). - /// \tparam V One of the TOML node or value types (or a type promotable to one). - /// \param val The value used to initialize node 0. - /// \param vals The values used to initialize nodes 1...N. - template + /// \tparam ElemType One of the TOML node or value types (or a type promotable to one). + /// \tparam ElemTypes One of the TOML node or value types (or a type promotable to one). + /// \param val The node or value used to initialize element 0. + /// \param vals The nodes or values used to initialize elements 1...N. + template 0_sz) + || !std::is_same_v, array> + >> TOML_NODISCARD_CTOR - explicit array(U&& val, V&&... vals) + explicit array(ElemType&& val, ElemTypes&&... vals) { - values.reserve(sizeof...(V) + 1_sz); - values.emplace_back(impl::make_node(std::forward(val))); - if constexpr (sizeof...(V) > 0) + elements.reserve(sizeof...(ElemTypes) + 1_sz); + emplace_back_if_not_empty_view(std::forward(val)); + if constexpr (sizeof...(ElemTypes) > 0) { ( - values.emplace_back(impl::make_node(std::forward(vals))), + emplace_back_if_not_empty_view(std::forward(vals)), ... ); } + + #if TOML_LIFETIME_HOOKS + lh_ctor(); + #endif } - /// \brief Move-assignment operator. - array& operator= (array&& rhs) noexcept; - - array(const array&) = delete; - array& operator= (const array&) = delete; - - /// \brief Always returns node_type::array for array nodes. [[nodiscard]] node_type type() const noexcept override; - /// \brief Always returns `false` for array nodes. [[nodiscard]] bool is_table() const noexcept override; - /// \brief Always returns `true` for array nodes. [[nodiscard]] bool is_array() const noexcept override; - /// \brief Always returns `false` for array nodes. [[nodiscard]] bool is_value() const noexcept override; [[nodiscard]] array* as_array() noexcept override; [[nodiscard]] const array* as_array() const noexcept override; + [[nodiscard]] bool is_array_of_tables() const noexcept override; - /// \brief Checks if the array contains nodes of only one type. - /// - /// \detail \cpp - /// auto arr = toml::array{ 1, 2, 3 }; - /// std::cout << "homogenous: "sv << arr.is_homogeneous() << std::endl; - /// std::cout << "all doubles: "sv << arr.is_homogeneous() << std::endl; - /// std::cout << "all arrays: "sv << arr.is_homogeneous() << std::endl; - /// std::cout << "all integers: "sv << arr.is_homogeneous() << std::endl; - /// - /// \ecpp - /// - /// \out - /// homogeneous: true - /// all doubles: false - /// all arrays: false - /// all integers: true - /// \eout - /// - /// \tparam T A TOML node type.
- /// Explicitly specified: "is every node a T?"
- /// Left as `void`: "is every node the same type?" - /// - /// \returns True if the array was homogeneous. - /// - /// \remarks Always returns `false` for empty arrays. - template - [[nodiscard]] bool is_homogeneous() const noexcept + [[nodiscard]] bool is_homogeneous(node_type ntype) const noexcept override; + [[nodiscard]] bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept override; + [[nodiscard]] bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept override; + template + [[nodiscard]] + bool is_homogeneous() const noexcept { - if (values.empty()) - return false; - - if constexpr (std::is_same_v) - { - const auto type = values[0]->type(); - for (size_t i = 1; i < values.size(); i++) - if (values[i]->type() != type) - return false; - } - else - { - for (auto& v : values) - if (!v->is()) - return false; - } - return true; + using type = impl::unwrap_node; + static_assert( + std::is_void_v + || ((impl::is_native || impl::is_one_of) && !impl::is_cvref), + "The template type argument of array::is_homogeneous() must be void or one of:" + TOML_SA_UNWRAPPED_NODE_TYPE_LIST + ); + return is_homogeneous(impl::node_type_of); } - /// \brief Returns true if this array contains only tables. - [[nodiscard]] TOML_ALWAYS_INLINE - bool is_array_of_tables() const noexcept override - { - return is_homogeneous(); - } - - /// \brief Gets a reference to the node at a specific index. + /// \brief Gets a reference to the element at a specific index. [[nodiscard]] node& operator[] (size_t index) noexcept; - /// \brief Gets a reference to the node at a specific index. + /// \brief Gets a reference to the element at a specific index. [[nodiscard]] const node& operator[] (size_t index) const noexcept; - /// \brief Returns a reference to the first node in the array. + /// \brief Returns a reference to the first element in the array. [[nodiscard]] node& front() noexcept; - /// \brief Returns a reference to the first node in the array. + /// \brief Returns a reference to the first element in the array. [[nodiscard]] const node& front() const noexcept; - /// \brief Returns a reference to the last node in the array. + /// \brief Returns a reference to the last element in the array. [[nodiscard]] node& back() noexcept; - /// \brief Returns a reference to the last node in the array. + /// \brief Returns a reference to the last element in the array. [[nodiscard]] const node& back() const noexcept; - /// \brief Returns an iterator to the first node. + /// \brief Returns an iterator to the first element. [[nodiscard]] iterator begin() noexcept; - /// \brief Returns an iterator to the first node. + /// \brief Returns an iterator to the first element. [[nodiscard]] const_iterator begin() const noexcept; - /// \brief Returns an iterator to the first node. + /// \brief Returns an iterator to the first element. [[nodiscard]] const_iterator cbegin() const noexcept; - /// \brief Returns an iterator to one-past-the-last node. + /// \brief Returns an iterator to one-past-the-last element. [[nodiscard]] iterator end() noexcept; - /// \brief Returns an iterator to one-past-the-last node. + /// \brief Returns an iterator to one-past-the-last element. [[nodiscard]] const_iterator end() const noexcept; - /// \brief Returns an iterator to one-past-the-last node. + /// \brief Returns an iterator to one-past-the-last element. [[nodiscard]] const_iterator cend() const noexcept; /// \brief Returns true if the array is empty. [[nodiscard]] bool empty() const noexcept; - /// \brief Returns the number of nodes in the array. + /// \brief Returns the number of elements in the array. [[nodiscard]] size_t size() const noexcept; - /// \brief Reserves internal storage capacity up to a pre-determined number of nodes. + /// \brief Reserves internal storage capacity up to a pre-determined number of elements. void reserve(size_t new_capacity); - /// \brief Removes all nodes from the array. + /// \brief Removes all elements from the array. void clear() noexcept; - /// \brief Returns the maximum number of nodes that can be stored in an array on the current platform. + /// \brief Returns the maximum number of elements that can be stored in an array on the current platform. [[nodiscard]] size_t max_size() const noexcept; - /// \brief Returns the current max number of nodes that may be held in the array's internal storage. + /// \brief Returns the current max number of elements that may be held in the array's internal storage. [[nodiscard]] size_t capacity() const noexcept; /// \brief Requests the removal of any unused internal storage capacity. void shrink_to_fit(); - /// \brief Inserts a new node at a specific position in the array. + /// \brief Inserts a new element at a specific position in the array. /// /// \detail \cpp /// auto arr = toml::array{ 1, 3 }; /// arr.insert(arr.cbegin() + 1, "two"); /// arr.insert(arr.cend(), toml::array{ 4, 5 }); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, "two", 3, [4, 5]] + /// [ 1, 'two', 3, [ 4, 5 ] ] /// \eout /// - /// \tparam U One of the TOML node or value types (or a type promotable to one). - /// \param pos The insertion position. - /// \param val The value being inserted. + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type + /// (or a type promotable to one). + /// \param pos The insertion position. + /// \param val The node or value being inserted. /// - /// \returns An iterator to the inserted value. - template - iterator insert(const_iterator pos, U&& val) noexcept + /// \returns Valid input:
+ /// An iterator to the newly-inserted element. + ///

+ /// `val` is an empty toml::node_view:
+ /// end() + /// + /// \attention The return value will always be `end()` if the input value was an empty toml::node_view, + /// because no insertion can take place. This is the only circumstance in which this can occur. + template + iterator insert(const_iterator pos, ElemType&& val) noexcept { - return { values.emplace(pos.raw_, impl::make_node(std::forward(val))) }; + if constexpr (impl::is_node_view) + { + if (!val) + return end(); + } + return { elements.emplace(pos.raw_, impl::make_node(std::forward(val))) }; } - /// \brief Repeatedly inserts a value starting at a specific position in the array. + /// \brief Repeatedly inserts a new element starting at a specific position in the array. /// /// \detail \cpp /// auto arr = toml::array{ @@ -481,372 +524,419 @@ namespace toml /// "and immediately we knew peace was never an option." /// }; /// arr.insert(arr.cbegin() + 1, 3, "honk"); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out /// [ - /// "with an evil twinkle in its eye the goose said", - /// "honk", - /// "honk", - /// "honk", - /// "and immediately we knew peace was never an option." + /// 'with an evil twinkle in its eye the goose said', + /// 'honk', + /// 'honk', + /// 'honk', + /// 'and immediately we knew peace was never an option.' /// ] /// \eout /// - /// \tparam U One of the TOML value types (or a type promotable to one). - /// \param pos The insertion position. - /// \param count The number of times the value should be inserted. - /// \param val The value being inserted. + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type + /// (or a type promotable to one). + /// \param pos The insertion position. + /// \param count The number of times the node or value should be inserted. + /// \param val The node or value being inserted. /// - /// \returns An iterator to the first inserted value (or a copy of `pos` if count was 0). - template - iterator insert(const_iterator pos, size_t count, U&& val) noexcept + /// \returns Valid input:
+ /// An iterator to the newly-inserted element. + ///

+ /// `count == 0`:
+ /// A copy of pos + ///

+ /// `val` is an empty toml::node_view:
+ /// end() + /// + /// \attention The return value will always be `end()` if the input value was an empty toml::node_view, + /// because no insertion can take place. This is the only circumstance in which this can occur. + template + iterator insert(const_iterator pos, size_t count, ElemType&& val) noexcept { + if constexpr (impl::is_node_view) + { + if (!val) + return end(); + } switch (count) { - case 0: return { values.begin() + (pos.raw_ - values.cbegin()) }; - case 1: return insert(pos, std::forward(val)); + case 0: return { elements.begin() + (pos.raw_ - elements.cbegin()) }; + case 1: return insert(pos, std::forward(val)); default: { - const auto start_idx = static_cast(pos.raw_ - values.cbegin()); + const auto start_idx = static_cast(pos.raw_ - elements.cbegin()); preinsertion_resize(start_idx, count); size_t i = start_idx; for (size_t e = start_idx + count - 1_sz; i < e; i++) - values[i].reset(impl::make_node(val)); + elements[i].reset(impl::make_node(val)); //# potentially move the initial value into the last element - values[i].reset(impl::make_node(std::forward(val))); - return { values.begin() + static_cast(start_idx) }; + elements[i].reset(impl::make_node(std::forward(val))); + return { elements.begin() + static_cast(start_idx) }; } } } - /// \brief Inserts a range of values into the array at a specific position. + /// \brief Inserts a range of elements into the array at a specific position. /// /// \tparam Iter An iterator type. Must satisfy ForwardIterator. /// \param pos The insertion position. - /// \param first Iterator to the first value being inserted. - /// \param last Iterator to the one-past-the-last value being inserted. + /// \param first Iterator to the first node or value being inserted. + /// \param last Iterator to the one-past-the-last node or value being inserted. /// - /// \returns An iterator to the first inserted value (or a copy of `pos` if `first` == `last`). + /// \returns Valid input:
+ /// An iterator to the first newly-inserted element. + ///

+ /// `first >= last`:
+ /// A copy of pos + ///

+ /// All objects in the range were empty toml::node_views:
+ /// A copy of pos template iterator insert(const_iterator pos, Iter first, Iter last) noexcept { - const auto count = std::distance(first, last); - switch (count) + const auto distance = std::distance(first, last); + if (distance <= 0) + return { elements.begin() + (pos.raw_ - elements.cbegin()) }; + else { - case 0: return { values.begin() + (pos.raw_ - values.cbegin()) }; - case 1: return insert(pos, *first); - default: + auto count = distance; + using deref_type = decltype(*first); + if constexpr (impl::is_node_view) { - const auto start_idx = static_cast(pos.raw_ - values.cbegin()); - preinsertion_resize(start_idx, count); - size_t i = start_idx; for (auto it = first; it != last; it++) - values[i].reset(impl::make_node(*it)); - return { values.begin() + static_cast(start_idx) }; + if (!(*it)) + count--; + if (!count) + return { elements.begin() + (pos.raw_ - elements.cbegin()) }; } - } - } - - /// \brief Inserts a range of values into the array at a specific position. - /// - /// \tparam U One of the TOML value types (or a type promotable to one). - /// \param pos The insertion position. - /// \param ilist An initializer list containing the values to be inserted. - /// - /// \returns An iterator to the first inserted value (or a copy of `pos` if `ilist` was empty). - template - iterator insert(const_iterator pos, std::initializer_list ilist) noexcept - { - switch (ilist.size()) - { - case 0: return { values.begin() + (pos.raw_ - values.cbegin()) }; - case 1: return insert(pos, *ilist.begin()); - default: + const auto start_idx = static_cast(pos.raw_ - elements.cbegin()); + preinsertion_resize(start_idx, static_cast(count)); + size_t i = start_idx; + for (auto it = first; it != last; it++) { - const auto start_idx = static_cast(pos.raw_ - values.cbegin()); - preinsertion_resize(start_idx, ilist.size()); - size_t i = start_idx; - for (auto& val : ilist) - values[i].reset(impl::make_node(val)); - return { values.begin() + static_cast(start_idx) }; + if constexpr (impl::is_node_view) + { + if (!(*it)) + continue; + } + if constexpr (std::is_rvalue_reference_v) + elements[i++].reset(impl::make_node(std::move(*it))); + else + elements[i++].reset(impl::make_node(*it)); } + return { elements.begin() + static_cast(start_idx) }; } } - /// \brief Emplaces a new value at a specific position in the array. + /// \brief Inserts a range of elements into the array at a specific position. + /// + /// \tparam ElemType toml::node_view, toml::table, toml::array, or a native TOML value type + /// (or a type promotable to one). + /// \param pos The insertion position. + /// \param ilist An initializer list containing the values to be inserted. + /// + /// \returns Valid input:
+ /// An iterator to the first newly-inserted element. + ///

+ /// `ilist.size() == 0`:
+ /// A copy of pos + ///

+ /// All objects in the list were empty toml::node_views:
+ /// A copy of pos + template + iterator insert(const_iterator pos, std::initializer_list ilist) noexcept + { + return insert(pos, ilist.begin(), ilist.end()); + } + + /// \brief Emplaces a new element at a specific position in the array. /// /// \detail \cpp /// auto arr = toml::array{ 1, 2 }; /// /// //add a string using std::string's substring constructor /// arr.emplace(arr.cbegin() + 1, "this is not a drill"sv, 14, 5); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, "drill", 2] + /// [ 1, 'drill', 2 ] /// \eout /// - /// \tparam U One of the TOML node or value types. - /// \tparam V Value constructor argument types. - /// \param pos The insertion position. - /// \param args Arguments to forward to the value's constructor. + /// \tparam ElemType toml::table, toml::array, or any native TOML value type. + /// \tparam Args Value constructor argument types. + /// \param pos The insertion position. + /// \param args Arguments to forward to the value's constructor. /// - /// \returns An iterator to the inserted value. + /// \returns An iterator to the inserted element. /// /// \remarks There is no difference between insert() and emplace() /// for trivial value types (floats, ints, bools). - template - iterator emplace(const_iterator pos, V&&... args) noexcept + template + iterator emplace(const_iterator pos, Args&&... args) noexcept { - using type = impl::unwrapped; + using type = impl::unwrap_node; static_assert( - impl::is_value_or_node, - "Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array" + (impl::is_native || impl::is_one_of) && !impl::is_cvref, + "Emplacement type parameter must be one of:" + TOML_SA_UNWRAPPED_NODE_TYPE_LIST ); - return { values.emplace(pos.raw_, new impl::node_of{ std::forward(args)...} ) }; + return { elements.emplace(pos.raw_, new impl::wrap_node{ std::forward(args)...} ) }; } - /// \brief Removes the specified node from the array. + /// \brief Removes the specified element from the array. /// /// \detail \cpp /// auto arr = toml::array{ 1, 2, 3 }; - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// arr.erase(arr.cbegin() + 1); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, 2, 3] - /// [1, 3] + /// [ 1, 2, 3 ] + /// [ 1, 3 ] /// \eout /// - /// \param pos Iterator to the node being erased. + /// \param pos Iterator to the element being erased. /// - /// \returns Iterator to the first node immediately following the removed node. + /// \returns Iterator to the first element immediately following the removed element. iterator erase(const_iterator pos) noexcept; - /// \brief Removes the nodes in the range [first, last) from the array. + /// \brief Removes the elements in the range [first, last) from the array. /// /// \detail \cpp /// auto arr = toml::array{ 1, "bad", "karma" 2 }; - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// arr.erase(arr.cbegin() + 1, arr.cbegin() + 3); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, "bad", "karma", 3] - /// [1, 3] + /// [ 1, 'bad', 'karma', 3 ] + /// [ 1, 3 ] /// \eout /// - /// \param first Iterator to the first node being erased. - /// \param last Iterator to the one-past-the-last node being erased. + /// \param first Iterator to the first element being erased. + /// \param last Iterator to the one-past-the-last element being erased. /// - /// \returns Iterator to the first node immediately following the last removed node. + /// \returns Iterator to the first element immediately following the last removed element. iterator erase(const_iterator first, const_iterator last) noexcept; - /// \brief Resizes the array. /// - /// \detail \cpp + /// \detail \godbolt{W5zqx3} + /// + /// \cpp /// auto arr = toml::array{ 1, 2, 3 }; - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// arr.resize(6, 42); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// arr.resize(2, 0); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, 2, 3] - /// [1, 2, 3, 42, 42, 42] - /// [1, 2] + /// [ 1, 2, 3 ] + /// [ 1, 2, 3, 42, 42, 42 ] + /// [ 1, 2 ] /// \eout /// - /// \tparam U One of the TOML value types (or a type promotable to one). + /// \tparam ElemType toml::node, toml::table, toml::array, or a native TOML value type + /// (or a type promotable to one). /// - /// \param new_size New number of elements the array will have. - /// \param default_init_val The value used to initialize new elements if the array needs to grow. - template - void resize(size_t new_size, U&& default_init_val) noexcept + /// \param new_size The number of elements the array will have after resizing. + /// \param default_init_val The node or value used to initialize new elements if the array needs to grow. + template + void resize(size_t new_size, ElemType&& default_init_val) noexcept { + static_assert( + !impl::is_node_view, + "The default element type argument to toml::array::resize may not be toml::node_view." + ); + if (!new_size) - values.clear(); - else if (new_size < values.size()) - values.resize(new_size); - else if (new_size > values.size()) - insert(cend(), new_size - values.size(), std::forward(default_init_val)); + elements.clear(); + else if (new_size < elements.size()) + elements.resize(new_size); + else if (new_size > elements.size()) + insert(cend(), new_size - elements.size(), std::forward(default_init_val)); } /// \brief Shrinks the array to the given size. /// - /// \detail \cpp + /// \detail \godbolt{rxEzK5} + /// + /// \cpp /// auto arr = toml::array{ 1, 2, 3 }; - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// arr.truncate(5); // no-op - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// arr.truncate(1); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, 2, 3] - /// [1, 2, 3] - /// [1] + /// [ 1, 2, 3 ] + /// [ 1, 2, 3 ] + /// [ 1] /// \eout /// /// \remarks Does nothing if the requested size is larger than or equal to the current size. void truncate(size_t new_size); - /// \brief Appends a new value to the end of the array. + /// \brief Appends a new element to the end of the array. /// /// \detail \cpp /// auto arr = toml::array{ 1, 2 }; /// arr.push_back(3); /// arr.push_back(4.0); /// arr.push_back(toml::array{ 5, "six"sv }); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, 2, 3, 4.0, [5, "six"]] + /// [ 1, 2, 3, 4.0, [ 5, 'six' ] ] /// \eout /// - /// \tparam U One of the TOML value types (or a type promotable to one). - /// \param val The value being added. + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type + /// \param val The node or value being added. /// - /// \returns A reference to the newly-constructed value node. - template - decltype(auto) push_back(U&& val) noexcept + /// \attention No insertion takes place if the input value is an empty toml::node_view. + /// This is the only circumstance in which this can occur. + template + void push_back(ElemType&& val) noexcept { - auto nde = impl::make_node(std::forward(val)); - values.emplace_back(nde); - return *nde; + emplace_back_if_not_empty_view(std::forward(val)); } - /// \brief Emplaces a new value at the end of the array. + /// \brief Emplaces a new element at the end of the array. /// /// \detail \cpp /// auto arr = toml::array{ 1, 2 }; /// arr.emplace_back(3, "four"sv); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, 2, [3, "four"]] + /// [ 1, 2, [ 3, 'four' ] ] /// \eout /// - /// \tparam U One of the TOML value types. - /// \tparam V Value constructor argument types. - /// \param args Arguments to forward to the value's constructor. + /// \tparam ElemType toml::table, toml::array, or a native TOML value type + /// \tparam Args Value constructor argument types. + /// \param args Arguments to forward to the value's constructor. /// - /// \returns A reference to the newly-constructed value node. + /// \returns A reference to the newly-constructed element. /// - /// \remarks There is no difference between push_back and emplace_back + /// \remarks There is no difference between push_back() and emplace_back() /// For trivial value types (floats, ints, bools). - template - decltype(auto) emplace_back(V&&... args) noexcept + template + decltype(auto) emplace_back(Args&&... args) noexcept { - using type = impl::unwrapped; + using type = impl::unwrap_node; static_assert( - impl::is_value_or_node, - "Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array" + (impl::is_native || impl::is_one_of) && !impl::is_cvref, + "Emplacement type parameter must be one of:" + TOML_SA_UNWRAPPED_NODE_TYPE_LIST ); - auto nde = new impl::node_of{ std::forward(args)... }; - values.emplace_back(nde); + auto nde = new impl::wrap_node{ std::forward(args)... }; + elements.emplace_back(nde); return *nde; } - /// \brief Removes the last node from the array. + /// \brief Removes the last element from the array. void pop_back() noexcept; - /// \brief Gets the node at a specific index. + /// \brief Gets the element at a specific index. /// /// \detail \cpp /// auto arr = toml::array{ 99, "bottles of beer on the wall" }; - /// std::cout << "node [0] exists: "sv << !!arr.get(0) << std::endl; - /// std::cout << "node [1] exists: "sv << !!arr.get(1) << std::endl; - /// std::cout << "node [2] exists: "sv << !!arr.get(2) << std::endl; - /// if (auto val = arr.get(0)) - /// std::cout << "node [0] was an "sv << val->type() << std::endl; + /// std::cout << "element [0] exists: "sv << !!arr.get(0) << "\n"; + /// std::cout << "element [1] exists: "sv << !!arr.get(1) << "\n"; + /// std::cout << "element [2] exists: "sv << !!arr.get(2) << "\n"; + /// if (toml::node* val = arr.get(0)) + /// std::cout << "element [0] is an "sv << val->type() << "\n"; /// /// \ecpp /// /// \out - /// node [0] exists: true - /// node [1] exists: true - /// node [2] exists: false - /// node [0] was an integer + /// element [0] exists: true + /// element [1] exists: true + /// element [2] exists: false + /// element [0] is an integer /// \eout /// - /// \param index The node's index. + /// \param index The element's index. /// - /// \returns A pointer to the node at the specified index if one existed, or nullptr. + /// \returns A pointer to the element at the specified index if one existed, or nullptr. [[nodiscard]] node* get(size_t index) noexcept; - /// \brief Gets the node at a specific index (const overload). + /// \brief Gets the element at a specific index (const overload). /// - /// \param index The node's index. + /// \param index The element's index. /// - /// \returns A pointer to the node at the specified index if one existed, or nullptr. + /// \returns A pointer to the element at the specified index if one existed, or nullptr. [[nodiscard]] const node* get(size_t index) const noexcept; - /// \brief Gets the node at a specific index if it is a particular type. + /// \brief Gets the element at a specific index if it is a particular type. /// /// \detail \cpp /// auto arr = toml::array{ 42, "is the meaning of life, apparently."sv }; - /// if (auto val = arr.get_as(0)) - /// std::cout << "node [0] was an integer with value "sv << **val << std::endl; + /// if (toml::value* val = arr.get_as(0)) + /// std::cout << "element [0] is an integer with value "sv << *val << "\n"; /// /// \ecpp /// /// \out - /// node [0] was an integer with value 42 + /// element [0] is an integer with value 42 /// \eout /// - /// \tparam T The node's type. - /// \param index The node's index. + /// \tparam ElemType toml::table, toml::array, or a native TOML value type + /// \param index The element's index. /// - /// \returns A pointer to the selected node if it existed and was of the specified type, or nullptr. - template - [[nodiscard]] impl::node_of* get_as(size_t index) noexcept + /// \returns A pointer to the selected element if it existed and was of the specified type, or nullptr. + template + [[nodiscard]] + impl::wrap_node* get_as(size_t index) noexcept { if (auto val = get(index)) - return val->as(); + return val->as(); return nullptr; } - /// \brief Gets the node at a specific index if it is a particular type (const overload). + /// \brief Gets the element at a specific index if it is a particular type (const overload). /// - /// \tparam T The node's type. - /// \param index The node's index. + /// \tparam ElemType toml::table, toml::array, or a native TOML value type + /// \param index The element's index. /// - /// \returns A pointer to the selected node if it existed and was of the specified type, or nullptr. - template - [[nodiscard]] const impl::node_of* get_as(size_t index) const noexcept + /// \returns A pointer to the selected element if it existed and was of the specified type, or nullptr. + template + [[nodiscard]] + const impl::wrap_node* get_as(size_t index) const noexcept { if (auto val = get(index)) - return val->as(); + return val->as(); return nullptr; } @@ -855,7 +945,7 @@ namespace toml /// \param lhs The LHS array. /// \param rhs The RHS array. /// - /// \returns True if the arrays contained the same values. + /// \returns True if the arrays contained the same elements. friend bool operator == (const array& lhs, const array& rhs) noexcept; /// \brief Inequality operator. @@ -863,17 +953,18 @@ namespace toml /// \param lhs The LHS array. /// \param rhs The RHS array. /// - /// \returns True if the arrays did not contain the same values. + /// \returns True if the arrays did not contain the same elements. friend bool operator != (const array& lhs, const array& rhs) noexcept; private: template - [[nodiscard]] static bool container_equality(const array& lhs, const T& rhs) noexcept + [[nodiscard]] + static bool container_equality(const array& lhs, const T& rhs) noexcept { - using elem_t = std::remove_const_t; + using element_type = std::remove_const_t; static_assert( - impl::is_value_or_promotable, + impl::is_native || impl::is_losslessly_convertible_to_native, "Container element type must be (or be promotable to) one of the TOML value types" ); @@ -885,7 +976,7 @@ namespace toml size_t i{}; for (auto& list_elem : rhs) { - const auto elem = lhs.get_as>(i++); + const auto elem = lhs.get_as>(i++); if (!elem || *elem != list_elem) return false; } @@ -901,7 +992,8 @@ namespace toml /// \brief Initializer list equality operator. template - [[nodiscard]] friend bool operator == (const array& lhs, const std::initializer_list& rhs) noexcept + [[nodiscard]] + friend bool operator == (const array& lhs, const std::initializer_list& rhs) noexcept { return container_equality(lhs, rhs); } @@ -909,7 +1001,8 @@ namespace toml /// \brief Vector equality operator. template - [[nodiscard]] friend bool operator == (const array& lhs, const std::vector& rhs) noexcept + [[nodiscard]] + friend bool operator == (const array& lhs, const std::vector& rhs) noexcept { return container_equality(lhs, rhs); } @@ -920,32 +1013,34 @@ namespace toml /// \detail \cpp /// /// auto arr = toml::array{ 1, 2, toml::array{ 3, 4, toml::array{ 5 } }, 6, toml::array{} }; - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// arr.flatten(); - /// std::cout << arr << std::endl; + /// std::cout << arr << "\n"; /// /// \ecpp /// /// \out - /// [1, 2, [3, 4, [5]], 6, []] - /// [1, 2, 3, 4, 5, 6] + /// [ 1, 2, [ 3, 4, [ 5 ] ], 6, [] ] + /// [ 1, 2, 3, 4, 5, 6 ] /// \eout /// /// \remarks Arrays inside child tables are not flattened. /// - /// A reference to the array. + /// \returns A reference to the array. array& flatten() &; /// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself (rvalue overload). - array&& flatten()&& + /// \returns An rvalue reference to the array. + array&& flatten() && { return static_cast(static_cast(*this).flatten()); } + /// \brief Prints the array out to a stream as formatted TOML. template friend std::basic_ostream& operator << (std::basic_ostream&, const array&); + // implemented in toml_default_formatter.h }; } - -TOML_POP_WARNINGS //TOML_DISABLE_VTABLE_WARNINGS +TOML_NAMESPACE_END diff --git a/deps/toml++/toml_array.hpp b/deps/toml++/toml_array.hpp index 411f28d..a65f78c 100644 --- a/deps/toml++/toml_array.hpp +++ b/deps/toml++/toml_array.hpp @@ -13,41 +13,104 @@ #include "toml_array.h" -TOML_PUSH_WARNINGS -TOML_DISABLE_SUGGEST_WARNINGS - -namespace toml +TOML_NAMESPACE_START { + #if TOML_LIFETIME_HOOKS + TOML_EXTERNAL_LINKAGE - void array::preinsertion_resize(size_t idx, size_t count) noexcept + void array::lh_ctor() noexcept { - const auto new_size = values.size() + count; - const auto inserting_at_end = idx == values.size(); - values.resize(new_size); - if (!inserting_at_end) - { - for (size_t r = new_size, e = idx + count, l = e; r-- > e; l--) - values[r] = std::move(values[l]); - } + TOML_ARRAY_CREATED; } TOML_EXTERNAL_LINKAGE - array::array() noexcept = default; + void array::lh_dtor() noexcept + { + TOML_ARRAY_DESTROYED; + } + + #endif + + TOML_EXTERNAL_LINKAGE + array::array() noexcept + { + #if TOML_LIFETIME_HOOKS + lh_ctor(); + #endif + } + + TOML_EXTERNAL_LINKAGE + array::array(const array& other) noexcept + : node{ other } + { + elements.reserve(other.elements.size()); + for (const auto& elem : other) + elements.emplace_back(impl::make_node(elem)); + + #if TOML_LIFETIME_HOOKS + lh_ctor(); + #endif + } TOML_EXTERNAL_LINKAGE array::array(array&& other) noexcept : node{ std::move(other) }, - values{ std::move(other.values) } - {} + elements{ std::move(other.elements) } + { + #if TOML_LIFETIME_HOOKS + lh_ctor(); + #endif + } + + TOML_EXTERNAL_LINKAGE + array& array::operator= (const array& rhs) noexcept + { + if (&rhs != this) + { + node::operator=(rhs); + elements.clear(); + elements.reserve(rhs.elements.size()); + for (const auto& elem : rhs) + elements.emplace_back(impl::make_node(elem)); + } + return *this; + } TOML_EXTERNAL_LINKAGE array& array::operator= (array&& rhs) noexcept { - node::operator=(std::move(rhs)); - values = std::move(rhs.values); + if (&rhs != this) + { + node::operator=(std::move(rhs)); + elements = std::move(rhs.elements); + } return *this; } + TOML_EXTERNAL_LINKAGE + array::~array() noexcept + { + #if TOML_LIFETIME_HOOKS + lh_dtor(); + #endif + } + + TOML_EXTERNAL_LINKAGE + void array::preinsertion_resize(size_t idx, size_t count) noexcept + { + TOML_ASSERT(idx <= elements.size()); + TOML_ASSERT(count >= 1_sz); + const auto old_size = elements.size(); + const auto new_size = old_size + count; + const auto inserting_at_end = idx == old_size; + elements.resize(new_size); + if (!inserting_at_end) + { + for(size_t left = old_size, right = new_size - 1_sz; left --> idx; right--) + elements[right] = std::move(elements[left]); + } + } + #define TOML_MEMBER_ATTR(attr) TOML_EXTERNAL_LINKAGE TOML_ATTR(attr) TOML_MEMBER_ATTR(const) node_type array::type() const noexcept { return node_type::array; } @@ -57,69 +120,122 @@ namespace toml TOML_MEMBER_ATTR(const) const array* array::as_array() const noexcept { return this; } TOML_MEMBER_ATTR(const) array* array::as_array() noexcept { return this; } - TOML_MEMBER_ATTR(pure) const node& array::operator[] (size_t index) const noexcept { return *values[index]; } - TOML_MEMBER_ATTR(pure) node& array::operator[] (size_t index) noexcept { return *values[index]; } + TOML_MEMBER_ATTR(pure) const node& array::operator[] (size_t index) const noexcept { return *elements[index]; } + TOML_MEMBER_ATTR(pure) node& array::operator[] (size_t index) noexcept { return *elements[index]; } - TOML_MEMBER_ATTR(pure) const node& array::front() const noexcept { return *values.front(); } - TOML_MEMBER_ATTR(pure) const node& array::back() const noexcept { return *values.back(); } - TOML_MEMBER_ATTR(pure) node& array::front() noexcept { return *values.front(); } - TOML_MEMBER_ATTR(pure) node& array::back() noexcept { return *values.back(); } + TOML_MEMBER_ATTR(pure) const node& array::front() const noexcept { return *elements.front(); } + TOML_MEMBER_ATTR(pure) const node& array::back() const noexcept { return *elements.back(); } + TOML_MEMBER_ATTR(pure) node& array::front() noexcept { return *elements.front(); } + TOML_MEMBER_ATTR(pure) node& array::back() noexcept { return *elements.back(); } - TOML_MEMBER_ATTR(pure) array::const_iterator array::begin() const noexcept { return { values.begin() }; } - TOML_MEMBER_ATTR(pure) array::const_iterator array::end() const noexcept { return { values.end() }; } - TOML_MEMBER_ATTR(pure) array::const_iterator array::cbegin() const noexcept { return { values.cbegin() }; } - TOML_MEMBER_ATTR(pure) array::const_iterator array::cend() const noexcept { return { values.cend() }; } - TOML_MEMBER_ATTR(pure) array::iterator array::begin() noexcept { return { values.begin() }; } - TOML_MEMBER_ATTR(pure) array::iterator array::end() noexcept { return { values.end() }; } + TOML_MEMBER_ATTR(pure) array::const_iterator array::begin() const noexcept { return { elements.begin() }; } + TOML_MEMBER_ATTR(pure) array::const_iterator array::end() const noexcept { return { elements.end() }; } + TOML_MEMBER_ATTR(pure) array::const_iterator array::cbegin() const noexcept { return { elements.cbegin() }; } + TOML_MEMBER_ATTR(pure) array::const_iterator array::cend() const noexcept { return { elements.cend() }; } + TOML_MEMBER_ATTR(pure) array::iterator array::begin() noexcept { return { elements.begin() }; } + TOML_MEMBER_ATTR(pure) array::iterator array::end() noexcept { return { elements.end() }; } - TOML_MEMBER_ATTR(pure) size_t array::size() const noexcept { return values.size(); } - TOML_MEMBER_ATTR(pure) size_t array::capacity() const noexcept { return values.capacity(); } - TOML_MEMBER_ATTR(pure) bool array::empty() const noexcept { return values.empty(); } - TOML_MEMBER_ATTR(const) size_t array::max_size() const noexcept { return values.max_size(); } + TOML_MEMBER_ATTR(pure) size_t array::size() const noexcept { return elements.size(); } + TOML_MEMBER_ATTR(pure) size_t array::capacity() const noexcept { return elements.capacity(); } + TOML_MEMBER_ATTR(pure) bool array::empty() const noexcept { return elements.empty(); } + TOML_MEMBER_ATTR(const) size_t array::max_size() const noexcept { return elements.max_size(); } - TOML_EXTERNAL_LINKAGE void array::reserve(size_t new_capacity) { values.reserve(new_capacity); } - TOML_EXTERNAL_LINKAGE void array::clear() noexcept { values.clear(); } - TOML_EXTERNAL_LINKAGE void array::shrink_to_fit() { values.shrink_to_fit(); } + TOML_EXTERNAL_LINKAGE void array::reserve(size_t new_capacity) { elements.reserve(new_capacity); } + TOML_EXTERNAL_LINKAGE void array::clear() noexcept { elements.clear(); } + TOML_EXTERNAL_LINKAGE void array::shrink_to_fit() { elements.shrink_to_fit(); } #undef TOML_MEMBER_ATTR + TOML_EXTERNAL_LINKAGE + bool array::is_homogeneous(node_type ntype) const noexcept + { + if (elements.empty()) + return false; + + if (ntype == node_type::none) + ntype = elements[0]->type(); + + for (const auto& val : elements) + if (val->type() != ntype) + return false; + + return true; + } + + namespace impl + { + template + TOML_INTERNAL_LINKAGE + bool array_is_homogeneous(T& elements, node_type ntype, U& first_nonmatch) noexcept + { + if (elements.empty()) + { + first_nonmatch = {}; + return false; + } + if (ntype == node_type::none) + ntype = elements[0]->type(); + for (const auto& val : elements) + { + if (val->type() != ntype) + { + first_nonmatch = val.get(); + return false; + } + } + return true; + } + } + + TOML_EXTERNAL_LINKAGE + bool array::is_homogeneous(node_type ntype, toml::node*& first_nonmatch) noexcept + { + return impl::array_is_homogeneous(elements, ntype, first_nonmatch); + } + + TOML_EXTERNAL_LINKAGE + bool array::is_homogeneous(node_type ntype, const toml::node*& first_nonmatch) const noexcept + { + return impl::array_is_homogeneous(elements, ntype, first_nonmatch); + } + TOML_EXTERNAL_LINKAGE void array::truncate(size_t new_size) { - if (new_size < values.size()) - values.resize(new_size); + if (new_size < elements.size()) + elements.resize(new_size); } TOML_EXTERNAL_LINKAGE array::iterator array::erase(const_iterator pos) noexcept { - return { values.erase(pos.raw_) }; + return { elements.erase(pos.raw_) }; } TOML_EXTERNAL_LINKAGE array::iterator array::erase(const_iterator first, const_iterator last) noexcept { - return { values.erase(first.raw_, last.raw_) }; + return { elements.erase(first.raw_, last.raw_) }; } TOML_EXTERNAL_LINKAGE void array::pop_back() noexcept { - values.pop_back(); + elements.pop_back(); } TOML_EXTERNAL_LINKAGE TOML_ATTR(pure) node* array::get(size_t index) noexcept { - return index < values.size() ? values[index].get() : nullptr; + return index < elements.size() ? elements[index].get() : nullptr; } TOML_EXTERNAL_LINKAGE TOML_ATTR(pure) const node* array::get(size_t index) const noexcept { - return index < values.size() ? values[index].get() : nullptr; + return index < elements.size() ? elements[index].get() : nullptr; } TOML_API @@ -128,17 +244,17 @@ namespace toml { if (&lhs == &rhs) return true; - if (lhs.values.size() != rhs.values.size()) + if (lhs.elements.size() != rhs.elements.size()) return false; - for (size_t i = 0, e = lhs.values.size(); i < e; i++) + for (size_t i = 0, e = lhs.elements.size(); i < e; i++) { - const auto lhs_type = lhs.values[i]->type(); - const node& rhs_ = *rhs.values[i]; + const auto lhs_type = lhs.elements[i]->type(); + const node& rhs_ = *rhs.elements[i]; const auto rhs_type = rhs_.type(); if (lhs_type != rhs_type) return false; - const bool equal = lhs.values[i]->visit([&](const auto& lhs_) noexcept + const bool equal = lhs.elements[i]->visit([&](const auto& lhs_) noexcept { return lhs_ == *reinterpret_cast*>(&rhs_); }); @@ -159,9 +275,9 @@ namespace toml size_t array::total_leaf_count() const noexcept { size_t leaves{}; - for (size_t i = 0, e = values.size(); i < e; i++) + for (size_t i = 0, e = elements.size(); i < e; i++) { - auto arr = values[i]->as_array(); + auto arr = elements[i]->as_array(); leaves += arr ? arr->total_leaf_count() : 1_sz; } return leaves; @@ -172,29 +288,29 @@ namespace toml { for (size_t i = 0, e = child.size(); i < e; i++) { - auto type = child.values[i]->type(); + auto type = child.elements[i]->type(); if (type == node_type::array) { - array& arr = *reinterpret_cast(child.values[i].get()); + array& arr = *reinterpret_cast(child.elements[i].get()); if (!arr.empty()) flatten_child(std::move(arr), dest_index); } else - values[dest_index++] = std::move(child.values[i]); + elements[dest_index++] = std::move(child.elements[i]); } } TOML_EXTERNAL_LINKAGE array& array::flatten() & { - if (values.empty()) + if (elements.empty()) return *this; bool requires_flattening = false; - size_t size_after_flattening = values.size(); - for (size_t i = values.size(); i --> 0_sz;) + size_t size_after_flattening = elements.size(); + for (size_t i = elements.size(); i --> 0_sz;) { - auto arr = values[i]->as_array(); + auto arr = elements[i]->as_array(); if (!arr) continue; size_after_flattening--; //discount the array itself @@ -205,25 +321,25 @@ namespace toml size_after_flattening += leaf_count; } else - values.erase(values.cbegin() + static_cast(i)); + elements.erase(elements.cbegin() + static_cast(i)); } if (!requires_flattening) return *this; - values.reserve(size_after_flattening); + elements.reserve(size_after_flattening); size_t i = 0; - while (i < values.size()) + while (i < elements.size()) { - auto arr = values[i]->as_array(); + auto arr = elements[i]->as_array(); if (!arr) { i++; continue; } - std::unique_ptr arr_storage = std::move(values[i]); + std::unique_ptr arr_storage = std::move(elements[i]); const auto leaf_count = arr->total_leaf_count(); if (leaf_count > 1_sz) preinsertion_resize(i + 1_sz, leaf_count - 1_sz); @@ -232,6 +348,11 @@ namespace toml return *this; } -} -TOML_POP_WARNINGS // TOML_DISABLE_SUGGEST_WARNINGS + TOML_EXTERNAL_LINKAGE + bool array::is_array_of_tables() const noexcept + { + return is_homogeneous(node_type::table); + } +} +TOML_NAMESPACE_END diff --git a/deps/toml++/toml_common.h b/deps/toml++/toml_common.h index 2590041..8919f6c 100644 --- a/deps/toml++/toml_common.h +++ b/deps/toml++/toml_common.h @@ -6,31 +6,30 @@ #pragma once #include "toml_preprocessor.h" -////////// INCLUDES +//#==================================================================================================================== +//# INCLUDES +//#==================================================================================================================== -TOML_PUSH_WARNINGS -TOML_DISABLE_ALL_WARNINGS - -#if __has_include() - #include -#endif +TOML_DISABLE_WARNINGS #include -#include //memcpy, memset +#include +#include +#include +#include +#include #include #include #include #include #include #include -#ifndef TOML_OPTIONAL_TYPE +#if !TOML_HAS_CUSTOM_OPTIONAL_TYPE #include #endif - -TOML_POP_WARNINGS - -#if TOML_CHAR_8_STRINGS && !defined(__cpp_lib_char8_t) - #error toml++ requires implementation support to use char8_t strings, but yours does not provide it. +#if TOML_HAS_INCLUDE() + #include #endif +TOML_ENABLE_WARNINGS #ifdef __cpp_lib_launder #define TOML_LAUNDER(x) std::launder(x) @@ -38,84 +37,205 @@ TOML_POP_WARNINGS #define TOML_LAUNDER(x) x #endif -////////// FORWARD DECLARATIONS & TYPEDEFS +//#==================================================================================================================== +//# ENVIRONMENT GROUND-TRUTHS +//#==================================================================================================================== -/// \brief The root namespace for all toml++ functions and types. -namespace toml +#ifndef DOXYGEN +#ifndef TOML_DISABLE_ENVIRONMENT_CHECKS +#define TOML_ENV_MESSAGE \ + "If you're seeing this error it's because you're building toml++ for an environment that doesn't conform to " \ + "one of the 'ground truths' assumed by the library. Essentially this just means that I don't have the " \ + "resources to test on more platforms, but I wish I did! You can try disabling the checks by defining " \ + "TOML_DISABLE_ENVIRONMENT_CHECKS, but your mileage may vary. Please consider filing an issue at " \ + "https://github.com/marzer/tomlplusplus/issues to help me improve support for your target environment. Thanks!" + +static_assert(CHAR_BIT == 8, TOML_ENV_MESSAGE); +static_assert(FLT_RADIX == 2, TOML_ENV_MESSAGE); +static_assert('A' == 65, TOML_ENV_MESSAGE); +static_assert(sizeof(double) == 8, TOML_ENV_MESSAGE); +static_assert(std::numeric_limits::is_iec559, TOML_ENV_MESSAGE); +static_assert(std::numeric_limits::digits == 53, TOML_ENV_MESSAGE); +static_assert(std::numeric_limits::digits10 == 15, TOML_ENV_MESSAGE); + +#undef TOML_ENV_MESSAGE +#endif // !TOML_DISABLE_ENVIRONMENT_CHECKS +#endif // !DOXYGEN + +//#==================================================================================================================== +//# UNDOCUMENTED TYPEDEFS AND FORWARD DECLARATIONS +//#==================================================================================================================== + +#ifndef DOXYGEN // undocumented forward declarations are hidden from doxygen because they fuck it up =/ + +namespace toml // non-abi namespace; this is not an error { using namespace std::string_literals; using namespace std::string_view_literals; - using size_t = std::size_t; - using ptrdiff_t = std::ptrdiff_t; + using ::std::size_t; + using ::std::intptr_t; + using ::std::uintptr_t; + using ::std::ptrdiff_t; + using ::std::nullptr_t; + using ::std::int8_t; + using ::std::int16_t; + using ::std::int32_t; + using ::std::int64_t; + using ::std::uint8_t; + using ::std::uint16_t; + using ::std::uint32_t; + using ::std::uint64_t; + using ::std::uint_least32_t; + using ::std::uint_least64_t; - [[nodiscard]] - TOML_ATTR(const) - TOML_ALWAYS_INLINE - TOML_CONSTEVAL size_t operator"" _sz(unsigned long long n) noexcept - { - return static_cast(n); - } - - #if TOML_CHAR_8_STRINGS - - using string_char = char8_t; - using string = std::u8string; - using string_view = std::u8string_view; - - #else - - /// \brief The base character type for keys and string values. - /// \remarks This will be an alias for char8_t if #TOML_CHAR_8_STRINGS is enabled. + // legacy typedefs using string_char = char; - - /// \brief The string type for keys and string values. - /// \remarks This will be an alias for std::u8string if #TOML_CHAR_8_STRINGS is enabled. using string = std::string; - - /// \brief The string type for keys and string values. - /// \remarks This will be an alias for std::u8string_view if #TOML_CHAR_8_STRINGS is enabled. using string_view = std::string_view; +} - #endif - - TOML_PUSH_WARNINGS - TOML_DISABLE_PADDING_WARNINGS - TOML_DISABLE_SHADOW_WARNINGS // false positive on gcc - - #if !TOML_DOXYGEN - - // foward declarations are hidden from doxygen - // because they fuck it up =/ +TOML_NAMESPACE_START // abi namespace +{ struct date; struct time; struct time_offset; + + TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt) + struct date_time; + TOML_ABI_NAMESPACE_END + class node; class array; class table; + template class node_view; template class value; template class default_formatter; template class json_formatter; - #ifdef TOML_OPTIONAL_TYPE - TOML_ABI_NAMESPACE_START(custopt) + [[nodiscard]] TOML_API bool operator == (const array& lhs, const array& rhs) noexcept; + [[nodiscard]] TOML_API bool operator != (const array& lhs, const array& rhs) noexcept; + [[nodiscard]] TOML_API bool operator == (const table& lhs, const table& rhs) noexcept; + [[nodiscard]] TOML_API bool operator != (const table& lhs, const table& rhs) noexcept; + + template + std::basic_ostream& operator << (std::basic_ostream&, const array&); + template + std::basic_ostream& operator << (std::basic_ostream&, const value&); + template + std::basic_ostream& operator << (std::basic_ostream&, const table&); + template + std::basic_ostream& operator << (std::basic_ostream&, default_formatter&); + template + std::basic_ostream& operator << (std::basic_ostream&, default_formatter&&); + template + std::basic_ostream& operator << (std::basic_ostream&, json_formatter&); + template + std::basic_ostream& operator << (std::basic_ostream&, json_formatter&&); + template + inline std::basic_ostream& operator << (std::basic_ostream&, const node_view&); + + namespace impl + { + template + using string_map = std::map>; // heterogeneous lookup + + template + using remove_cvref_t = std::remove_cv_t>; + + template + inline constexpr bool is_one_of = (false || ... || std::is_same_v); + + template + inline constexpr bool is_cvref = std::is_reference_v || std::is_const_v || std::is_volatile_v; + + template + inline constexpr bool is_wide_string = is_one_of< + std::decay_t, + const wchar_t*, + wchar_t*, + std::wstring_view, + std::wstring + >; + + template + inline constexpr bool dependent_false = false; + + #if TOML_WINDOWS_COMPAT + [[nodiscard]] TOML_API std::string narrow(std::wstring_view) noexcept; + [[nodiscard]] TOML_API std::wstring widen(std::string_view) noexcept; + #ifdef __cpp_lib_char8_t + [[nodiscard]] TOML_API std::wstring widen(std::u8string_view) noexcept; + #endif + #endif // TOML_WINDOWS_COMPAT + + #if TOML_ABI_NAMESPACES + #if TOML_EXCEPTIONS + TOML_ABI_NAMESPACE_START(ex) + #define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::ex::parser + #else + TOML_ABI_NAMESPACE_START(noex) + #define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::noex::parser + #endif + #else + #define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::parser + #endif + class parser; + TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS + } +} +TOML_NAMESPACE_END + +#endif // !DOXYGEN + +//#==================================================================================================================== +//# TYPEDEFS AND FORWARD DECLARATIONS +//#==================================================================================================================== + +/// \brief The root namespace for all toml++ functions and types. +namespace toml { } + +TOML_NAMESPACE_START // abi namespace +{ + /// \brief Convenience literal operators for working with toml++. + /// + /// \detail This namespace exists so you can safely hoist the toml++ literal operators into another scope + /// without dragging in everything from the toml namespace: \cpp + /// + /// #include + /// using namespace toml::literals; + /// + /// int main() + /// { + /// toml::table tbl = "vals = [1, 2, 3]"_toml; + /// + /// // ... do stuff with the table generated by the "_toml" literal ... + /// + /// return 0; + /// } + /// \ecpp + /// + inline namespace literals {} + + #if TOML_HAS_CUSTOM_OPTIONAL_TYPE + template + using optional = TOML_OPTIONAL_TYPE; #else - TOML_ABI_NAMESPACE_START(stdopt) + /// \brief The 'optional' type used throughout the library. + /// + /// \remarks By default this will be an alias for std::optional, but you can change the optional type + /// used by the library by defining #TOML_OPTIONAL_TYPE. + template + using optional = std::optional; #endif - struct date_time; - - TOML_ABI_NAMESPACE_END // TOML_OPTIONAL_TYPE - - #endif // !TOML_DOXYGEN - /// \brief TOML node type identifiers. enum class node_type : uint8_t { none, ///< Not-a-node. table, ///< The node is a toml::table. array, ///< The node is a toml::array. - string, ///< The node is a toml::value. + string, ///< The node is a toml::value. integer, ///< The node is a toml::value. floating_point, ///< The node is a toml::value. boolean, ///< The node is a toml::value. @@ -124,43 +244,465 @@ namespace toml date_time ///< The node is a toml::value. }; - TOML_POP_WARNINGS // TOML_DISABLE_PADDING_WARNINGS, TOML_DISABLE_SHADOW_WARNINGS - - #ifdef TOML_OPTIONAL_TYPE - - template - using optional = TOML_OPTIONAL_TYPE; - - #else - - /// \brief The 'optional' type used throughout the library. - /// - /// \remarks By default this will be a simple alias for std::optional, but you can change the optional type - /// used by the library by defining #TOML_OPTIONAL_TYPE. - template - using optional = std::optional; - - #endif - - #if TOML_LARGE_FILES - - using source_index = uint32_t; - - #else - - /// \brief The integer type used to tally line numbers and columns. - /// \remarks This will be an alias for uint32_t if #TOML_LARGE_FILES is enabled. - using source_index = uint16_t; - - #endif - /// \brief A pointer to a shared string resource containing a source path. using source_path_ptr = std::shared_ptr; + /// \brief Helper class for suppressing move-construction in single-argument array constructors. + /// + /// \detail \cpp + /// // desired result: [ [ 42 ] ] + /// auto bad = toml::array{ toml::array{ 42 } } + /// auto good = toml::array{ toml::inserter{ toml::array{ 42 } } } + /// std::cout << "bad: " << bad << "\n"; + /// std::cout << "good:" << good << "\n"; + /// \ecpp + /// + /// \out + /// bad: [ 42 ] + /// good: [ [ 42 ] ] + /// \eout + /// + /// \see toml::array + template + struct TOML_TRIVIAL_ABI inserter + { + T&& value; + }; + template inserter(T&&) -> inserter; +} +TOML_NAMESPACE_END + +//#==================================================================================================================== +//# VALUE AND NODE TRAITS +//#==================================================================================================================== + +TOML_IMPL_NAMESPACE_START +{ + // general value traits + // (as they relate to their equivalent native TOML type) + template + struct value_traits + { + using native_type = void; + static constexpr bool is_native = false; + static constexpr bool is_losslessly_convertible_to_native = false; + static constexpr bool can_represent_native = false; + static constexpr bool can_partially_represent_native = false; + static constexpr auto type = node_type::none; + }; + template struct value_traits : value_traits {}; + template struct value_traits : value_traits {}; + template struct value_traits : value_traits {}; + template struct value_traits : value_traits {}; + template struct value_traits : value_traits {}; + + // integer value traits + template + struct integer_value_limits + { + static constexpr auto min = (std::numeric_limits::min)(); + static constexpr auto max = (std::numeric_limits::max)(); + }; + template + struct integer_value_traits_base : integer_value_limits + { + using native_type = int64_t; + static constexpr bool is_native = std::is_same_v; + static constexpr bool is_signed = static_cast(-1) < T{}; // for impls not specializing std::is_signed + static constexpr auto type = node_type::integer; + static constexpr bool can_partially_represent_native = true; + }; + template + struct unsigned_integer_value_traits : integer_value_traits_base + { + static constexpr bool is_losslessly_convertible_to_native + = integer_value_limits::max <= 9223372036854775807ULL; + static constexpr bool can_represent_native = false; + + }; + template + struct signed_integer_value_traits : integer_value_traits_base + { + using native_type = int64_t; + static constexpr bool is_losslessly_convertible_to_native + = integer_value_limits::min >= (-9223372036854775807LL - 1LL) + && integer_value_limits::max <= 9223372036854775807LL; + static constexpr bool can_represent_native + = integer_value_limits::min <= (-9223372036854775807LL - 1LL) + && integer_value_limits::max >= 9223372036854775807LL; + }; + template ::is_signed> + struct integer_value_traits : signed_integer_value_traits {}; + template + struct integer_value_traits : unsigned_integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + #ifdef TOML_INT128 + template <> + struct integer_value_limits + { + static constexpr TOML_INT128 max = static_cast( + (TOML_UINT128{ 1u } << ((__SIZEOF_INT128__ * CHAR_BIT) - 1)) - 1 + ); + static constexpr TOML_INT128 min = -max - TOML_INT128{ 1 }; + }; + template <> + struct integer_value_limits + { + static constexpr TOML_UINT128 min = TOML_UINT128{}; + static constexpr TOML_UINT128 max = (2u * static_cast(integer_value_limits::max)) + 1u; + }; + template <> struct value_traits : integer_value_traits {}; + template <> struct value_traits : integer_value_traits {}; + #endif + #ifdef TOML_SMALL_INT_TYPE + template <> struct value_traits : signed_integer_value_traits {}; + #endif + static_assert(value_traits::is_native); + static_assert(value_traits::is_signed); + static_assert(value_traits::is_losslessly_convertible_to_native); + static_assert(value_traits::can_represent_native); + static_assert(value_traits::can_partially_represent_native); + + // float value traits + template + struct float_value_limits + { + static constexpr bool is_iec559 = std::numeric_limits::is_iec559; + static constexpr int digits = std::numeric_limits::digits; + static constexpr int digits10 = std::numeric_limits::digits10; + }; + template + struct float_value_traits : float_value_limits + { + using native_type = double; + static constexpr bool is_native = std::is_same_v; + static constexpr bool is_signed = true; + static constexpr bool is_losslessly_convertible_to_native + = float_value_limits::is_iec559 + && float_value_limits::digits <= 53 + && float_value_limits::digits10 <= 15; + static constexpr bool can_represent_native + = float_value_limits::is_iec559 + && float_value_limits::digits >= 53 // DBL_MANT_DIG + && float_value_limits::digits10 >= 15; // DBL_DIG + static constexpr bool can_partially_represent_native //32-bit float values + = float_value_limits::is_iec559 + && float_value_limits::digits >= 24 + && float_value_limits::digits10 >= 6; + static constexpr auto type = node_type::floating_point; + }; + template <> struct value_traits : float_value_traits {}; + template <> struct value_traits : float_value_traits {}; + template <> struct value_traits : float_value_traits {}; + template + struct extended_float_value_limits + { + static constexpr bool is_iec559 = true; + static constexpr int digits = mant_dig; + static constexpr int digits10 = dig; + }; + #ifdef TOML_FP16 + template <> struct float_value_limits : extended_float_value_limits<__FLT16_MANT_DIG__, __FLT16_DIG__> {}; + template <> struct value_traits : float_value_traits {}; + #endif + #ifdef TOML_FLOAT16 + template <> struct float_value_limits : extended_float_value_limits<__FLT16_MANT_DIG__, __FLT16_DIG__> {}; + template <> struct value_traits : float_value_traits {}; + #endif + #ifdef TOML_FLOAT128 + template <> struct float_value_limits : extended_float_value_limits<__FLT128_MANT_DIG__, __FLT128_DIG__> {}; + template <> struct value_traits : float_value_traits {}; + #endif + #ifdef TOML_SMALL_FLOAT_TYPE + template <> struct value_traits : float_value_traits {}; + #endif + static_assert(value_traits::is_native); + static_assert(value_traits::is_losslessly_convertible_to_native); + static_assert(value_traits::can_represent_native); + static_assert(value_traits::can_partially_represent_native); + + // string value traits + template + struct string_value_traits + { + using native_type = std::string; + static constexpr bool is_native = std::is_same_v; + static constexpr bool is_losslessly_convertible_to_native = true; + static constexpr bool can_represent_native + = !std::is_array_v + && (!std::is_pointer_v || std::is_const_v>); + static constexpr bool can_partially_represent_native = can_represent_native; + static constexpr auto type = node_type::string; + }; + template <> struct value_traits : string_value_traits {}; + template <> struct value_traits : string_value_traits {}; + template <> struct value_traits : string_value_traits {}; + template struct value_traits : string_value_traits {}; + template <> struct value_traits : string_value_traits {}; + template struct value_traits : string_value_traits {}; + #ifdef __cpp_lib_char8_t + template <> struct value_traits : string_value_traits {}; + template <> struct value_traits : string_value_traits {}; + template <> struct value_traits : string_value_traits {}; + template struct value_traits : string_value_traits {}; + template <> struct value_traits : string_value_traits {}; + template struct value_traits : string_value_traits {}; + #endif + #if TOML_WINDOWS_COMPAT + template + struct wstring_value_traits + { + using native_type = std::string; + static constexpr bool is_native = false; + static constexpr bool is_losslessly_convertible_to_native = true; //narrow + static constexpr bool can_represent_native = std::is_same_v; //widen + static constexpr bool can_partially_represent_native = can_represent_native; + static constexpr auto type = node_type::string; + }; + template <> struct value_traits : wstring_value_traits {}; + template <> struct value_traits : wstring_value_traits {}; + template <> struct value_traits : wstring_value_traits {}; + template struct value_traits : wstring_value_traits {}; + template <> struct value_traits : wstring_value_traits {}; + template struct value_traits : wstring_value_traits {}; + #endif + + // other native value traits + template + struct native_value_traits + { + using native_type = T; + static constexpr bool is_native = true; + static constexpr bool is_losslessly_convertible_to_native = true; + static constexpr bool can_represent_native = true; + static constexpr bool can_partially_represent_native = true; + static constexpr auto type = NodeType; + }; + template <> struct value_traits : native_value_traits {}; + template <> struct value_traits : native_value_traits {}; + template <> struct value_traits