├── .clang-format ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── cpptomlConfig.cmake.in ├── cpptoml.doxygen.in ├── examples ├── CMakeLists.txt ├── build_toml.cpp ├── conversions.cpp ├── parse.cpp └── parse_stdin.cpp ├── include └── cpptoml.h └── travis └── install_libcxx.sh /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # BasedOnStyle: LLVM 3 | AccessModifierOffset: -2 4 | ConstructorInitializerIndentWidth: 4 5 | AlignEscapedNewlinesLeft: false 6 | AlignTrailingComments: true 7 | AllowAllParametersOfDeclarationOnNextLine: true 8 | AllowShortBlocksOnASingleLine: false 9 | AllowShortIfStatementsOnASingleLine: false 10 | AllowShortLoopsOnASingleLine: false 11 | AllowShortFunctionsOnASingleLine: None 12 | AlwaysBreakTemplateDeclarations: true 13 | AlwaysBreakBeforeMultilineStrings: false 14 | BreakBeforeBinaryOperators: true 15 | BreakBeforeTernaryOperators: true 16 | BreakConstructorInitializersBeforeComma: false 17 | BinPackParameters: true 18 | ColumnLimit: 80 19 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 20 | DerivePointerAlignment: false 21 | ExperimentalAutoDetectBinPacking: false 22 | IndentCaseLabels: true 23 | IndentWrappedFunctionNames: false 24 | IndentFunctionDeclarationAfterType: true 25 | MaxEmptyLinesToKeep: 1 26 | KeepEmptyLinesAtTheStartOfBlocks: true 27 | NamespaceIndentation: None 28 | ObjCSpaceAfterProperty: false 29 | ObjCSpaceBeforeProtocolList: true 30 | PenaltyBreakBeforeFirstCallParameter: 19 31 | PenaltyBreakComment: 60 32 | PenaltyBreakString: 1000 33 | PenaltyBreakFirstLessLess: 120 34 | PenaltyExcessCharacter: 1000000 35 | PenaltyReturnTypeOnItsOwnLine: 60 36 | PointerAlignment: Left 37 | SpacesBeforeTrailingComments: 1 38 | Cpp11BracedListStyle: true 39 | Standard: Cpp11 40 | IndentWidth: 4 41 | TabWidth: 8 42 | UseTab: Never 43 | BreakBeforeBraces: Allman 44 | SpacesInParentheses: false 45 | SpacesInAngles: false 46 | SpaceInEmptyParentheses: false 47 | SpacesInCStyleCastParentheses: false 48 | SpacesInContainerLiterals: true 49 | SpaceBeforeAssignmentOperators: true 50 | ContinuationIndentWidth: 4 51 | SpaceBeforeParens: ControlStatements 52 | ... 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.o 3 | .ycm_* 4 | build/ 5 | bii/ 6 | bin/ 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/meta-cmake"] 2 | path = deps/meta-cmake 3 | url = https://github.com/meta-toolkit/meta-cmake.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | sudo: false 4 | 5 | compiler: 6 | - clang 7 | - gcc 8 | 9 | addons: 10 | apt: 11 | sources: 12 | - ubuntu-toolchain-r-test 13 | - llvm-toolchain-precise-3.6 14 | # I want to use this eventually, but right now it will prevent us 15 | # from installing g++-4.8 properly 16 | #- debian-sid 17 | packages: 18 | - g++-4.8 19 | #- libc++1 20 | #- libc++-dev 21 | #- libc++abi1 22 | #- libc++abi-dev 23 | - llvm-3.6-dev 24 | - clang-3.6 25 | 26 | install: 27 | - mkdir -p $HOME/lib 28 | - export LD_LIBRARY_PATH=$HOME/lib:$LD_LIBRARY_PATH 29 | - mkdir -p $HOME/bin 30 | - export PATH=$HOME/bin:$PATH 31 | - mkdir -p $HOME/include 32 | - export CPLUS_INCLUDE_PATH=$HOME/include:$CPLUS_INCLUDE_PATH 33 | - wget --no-check-certificate http://www.cmake.org/files/v3.2/cmake-3.2.2-Linux-x86_64.sh 34 | - sh cmake-3.2.2-Linux-x86_64.sh --prefix=$HOME --exclude-subdir 35 | # use g++-4.8 if g++ is our compiler 36 | - if [ "`echo $CXX`" == "g++" ]; then export CXX=g++-4.8; fi 37 | # install libc++ if tests are run with clang++ 38 | - if [ "`echo $CXX`" == "clang++" ]; then export CXX=clang++-3.6 && travis/install_libcxx.sh; fi 39 | 40 | script: 41 | - mkdir build 42 | - cd build 43 | - cmake .. -DCMAKE_BUILD_TYPE=Debug && make 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.0) 2 | project(cpptoml) 3 | 4 | set(cpptoml_VERSION 0.4.0) 5 | set(cpptoml_TOML_VERSION 0.4.0) 6 | 7 | include(CheckCXXCompilerFlag) 8 | include(CheckCXXSourceCompiles) 9 | include(CheckCXXSourceRuns) 10 | include(CMakePushCheckState) 11 | 12 | cmake_push_check_state() 13 | 14 | option(ENABLE_LIBCXX "Use libc++ for the C++ standard library" ON) 15 | option(CPPTOML_BUILD_EXAMPLES "Build examples" ON) 16 | 17 | set(CMAKE_EXPORT_COMPILE_COMMANDS 1) 18 | 19 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/deps/meta-cmake) 20 | 21 | if(UNIX OR MINGW) 22 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic") 23 | 24 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 25 | if(CMAKE_GENERATOR STREQUAL "Ninja") 26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") 27 | endif() 28 | 29 | if(ENABLE_LIBCXX) 30 | find_package(LIBCXX REQUIRED) 31 | set_libcxx_required_flags() 32 | endif() 33 | endif() 34 | endif() 35 | 36 | set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${CMAKE_CXX11_STANDARD_COMPILE_OPTION}") 37 | 38 | cmake_pop_check_state() 39 | 40 | add_library(cpptoml INTERFACE) 41 | 42 | target_include_directories(cpptoml INTERFACE 43 | $ 44 | $) 45 | 46 | if (LIBDL_LIBRARY) 47 | target_link_libraries(cpptoml INTERFACE ${LIBDL_LIBRARY}) 48 | endif() 49 | 50 | if (CXXABI_LIBRARY) 51 | target_link_libraries(cpptoml INTERFACE ${CXXABI_LIBRARY}) 52 | endif() 53 | 54 | if (CPPTOML_BUILD_EXAMPLES) 55 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 56 | add_subdirectory(examples) 57 | endif() 58 | 59 | find_package(Doxygen) 60 | if(DOXYGEN_FOUND AND NOT TARGET doc) 61 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cpptoml.doxygen.in 62 | ${CMAKE_CURRENT_BINARY_DIR}/cpptoml.doxygen @ONLY) 63 | add_custom_target(doc 64 | ${DOXYGEN_EXECUTABLE} 65 | ${CMAKE_CURRENT_BINARY_DIR}/cpptoml.doxygen 66 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 67 | endif() 68 | 69 | include(CMakePackageConfigHelpers) 70 | write_basic_package_version_file( 71 | ${CMAKE_CURRENT_BINARY_DIR}/cpptoml/cpptomlConfigVersion.cmake 72 | VERSION ${cpptoml_VERSION} 73 | COMPATIBILITY SameMajorVersion) 74 | configure_file(cmake/cpptomlConfig.cmake.in 75 | ${CMAKE_CURRENT_BINARY_DIR}/cpptoml/cpptomlConfig.cmake 76 | COPYONLY) 77 | 78 | install(TARGETS cpptoml 79 | EXPORT cpptoml-exports) 80 | install(FILES include/cpptoml.h 81 | DESTINATION include) 82 | install(EXPORT cpptoml-exports 83 | FILE cpptomlTargets.cmake 84 | DESTINATION lib/cmake/cpptoml) 85 | install(FILES 86 | ${CMAKE_CURRENT_BINARY_DIR}/cpptoml/cpptomlConfigVersion.cmake 87 | ${CMAKE_CURRENT_BINARY_DIR}/cpptoml/cpptomlConfig.cmake 88 | DESTINATION 89 | lib/cmake/cpptoml) 90 | 91 | export(EXPORT cpptoml-exports 92 | FILE ${CMAKE_CURRENT_BINARY_DIR}/cpptoml/cpptomlTargets.cmake) 93 | export(PACKAGE cpptoml) 94 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Chase Geigle 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpptoml 2 | A header-only library for parsing [TOML][toml] configuration files. 3 | 4 | Targets: [TOML v0.5.0][currver] as of August 2018. 5 | 6 | This includes support for the new DateTime format, inline tables, 7 | multi-line basic and raw strings, digit separators, hexadecimal integers, 8 | octal integers, binary integers, and float special values. 9 | 10 | Alternatives: 11 | - [Boost.toml][boost.toml] is a C++ implementation of a TOML parser using 12 | the Boost library. As of writing, it supports v0.5.0 as well. 13 | - [ctoml][ctoml] is a C++11 implementation of a TOML parser, but only 14 | supports v0.2.0. 15 | - [libtoml][libtoml] is a C implementation of a TOML parser, which can be 16 | linked to from your C++ programs easily. As of April 2016, it supports 17 | v0.4.0. 18 | - [tinytoml][tinytoml] is a C++11 implementation of a TOML parser, which 19 | also supports v0.4.0 as of November 2015. 20 | 21 | ## Build Status 22 | [![Build Status](https://travis-ci.org/skystrife/cpptoml.svg?branch=master)](https://travis-ci.org/skystrife/cpptoml) 23 | 24 | ## Test Results 25 | 26 | From [the toml-test suite][toml-test]: 27 | 28 | ``` 29 | 126 passed, 0 failed 30 | ``` 31 | 32 | We also currently maintain (but hopefully not indefinitely!) a [fork of the 33 | toml-test suite][toml-test-fork] that adds tests for features and 34 | clarifications that have been added to the TOML spec more recently than 35 | toml-test has been updated. We pass every test there. 36 | 37 | ``` 38 | 148 passed, 0 failed 39 | ``` 40 | 41 | # Compilation 42 | Requires a well conforming C++11 compiler. On OSX this means clang++ with 43 | libc++ and libc++abi (the default clang installed with XCode's command line 44 | tools is sufficient). 45 | 46 | On Linux, you should be able to use g++ >= 4.8.x, or clang++ with libc++ 47 | and libc++abi (if your package manager supplies this; most don't). 48 | 49 | Compiling the examples can be done with cmake: 50 | 51 | ``` 52 | mkdir build 53 | cd build 54 | cmake ../ 55 | make 56 | ``` 57 | 58 | # Example Usage 59 | To parse a configuration file from a file, you can do the following: 60 | 61 | ```cpp 62 | auto config = cpptoml::parse_file("config.toml"); 63 | ``` 64 | 65 | `parse_file()` returns a (shared pointer to a) `cpptoml::table`, which you 66 | can then query. It will throw an instance of `cpptoml::parse_exception` in 67 | the event that the file failed to parse, and the exception message should 68 | contain the line number the error occurred as well as a description of the 69 | error. 70 | 71 | ## Obtaining Basic Values 72 | You can find basic values like so: 73 | 74 | ```cpp 75 | auto val = config->get_as("my-int"); 76 | // val is a cpptoml::option 77 | 78 | if (val) 79 | { 80 | // *val is the integer value for the key "my-int" 81 | } 82 | else 83 | { 84 | // "my-int" either did not exist or was not an integer 85 | } 86 | ``` 87 | 88 | To simplify things, you can specify default a default value using the 89 | `value_or` function on the `option`: 90 | 91 | ```cpp 92 | auto baz = config->get_as("baz").value_or(0.5); 93 | // baz is now the double value for key "baz", if it exists, or 0.5 otherwise 94 | ``` 95 | 96 | cpptoml has extended support for dates and times beyond the TOML v0.4.0 97 | spec. Specifically, it supports 98 | 99 | - Local Date (`local_date`), which simply represents a date and lacks any time 100 | information, e.g. `1980-08-02`; 101 | - Local Time (`local_time`), which simply represents a time and lacks any 102 | date or zone information, e.g. `12:10:03.001`; 103 | - Local Date-time (`local_datetime`), which represents a date and a time, 104 | but lacks zone information, e.g. `1980-08-02T12:10:03.001`; 105 | - and Offset Date-time (`offset_datetime`), which represents a date, a 106 | time, and timezone information, e.g. `1980-08-02T12:10:03.001-07:00` 107 | 108 | Here are the fields of the date/time objects in cpptoml: 109 | 110 | - year (`local_date`, `local_datetime`, `offset_datetime`) 111 | - month (`local_date`, `local_datetime`, `offset_datetime`) 112 | - day (`local_date`, `local_datetime`, `offset_datetime`) 113 | - hour (`local_time`, `local_datetime`, `offset_datetime`) 114 | - minute (`local_time`, `local_datetime`, `offset_datetime`) 115 | - second (`local_time`, `local_datetime`, `offset_datetime`) 116 | - microsecond (`local_time`, `local_datetime`, `offset_datetime`) 117 | - hour\_offset (`offset_datetime`) 118 | - minute\_offset (`offset_datetime`) 119 | 120 | There are convenience functions `cpptoml::offset_datetime::from_zoned()` and 121 | `cpptoml::offset_datetime::from_utc()` to convert `struct tm`s to 122 | `cpptoml::offset_datetime`s. 123 | 124 | ## Nested Tables 125 | If you want to look up things in nested tables, there are two ways of doing 126 | this. Suppose you have the following structure: 127 | 128 | ```toml 129 | [first-table] 130 | key1 = 0.1 131 | key2 = 1284 132 | 133 | [first-table.inner] 134 | key3 = "hello world" 135 | ``` 136 | 137 | Here's an idiomatic way of obtaining all three keys' values: 138 | 139 | ```cpp 140 | auto config = cpptoml::parse_file("config.toml"); 141 | auto key1 = config->get_qualified_as("first-table.key1"); 142 | auto key2 = config->get_qualified_as("first-table.key2"); 143 | auto key3 = config->get_qualified_as("first-table.inner.key3"); 144 | ``` 145 | 146 | (Note that, because the TOML spec allows for "." to occur in a table name, 147 | you won't *always* be able to do this for any nested key, but in practice 148 | you should be fine.) 149 | 150 | A slightly more verbose way of getting them would be to first obtain the 151 | individual tables, and then query those individual tables for their keys 152 | like so: 153 | 154 | ```cpp 155 | auto config = cpptoml::parse_file("config.toml"); 156 | 157 | auto first = config->get_table("first-table"); 158 | auto key1 = first->get_as("key1"); 159 | auto key2 = first->get_as("key2"); 160 | 161 | auto inner = first->get_table("inner"); 162 | auto key3 = inner->get_as("key3"); 163 | ``` 164 | 165 | The function `get_table_qualified` also exists, so obtaining the inner 166 | table could be written as 167 | 168 | ```cpp 169 | auto inner2 = config->get_table_qualified("first-table.inner"); 170 | ``` 171 | 172 | ## Arrays of Values 173 | Suppose you had a configuration file like the following: 174 | 175 | ```toml 176 | arr = [1, 2, 3, 4, 5] 177 | mixed-arr = [[1, 2, 3, 4, 5], ["hello", "world"], [0.1, 1.1, 2.1]] 178 | ``` 179 | 180 | To obtain an array of values, you can do the following: 181 | 182 | ```cpp 183 | auto config = cpptoml::parse_file("config.toml"); 184 | 185 | auto vals = config->get_array_of("arr"); 186 | // vals is a cpptoml::option> 187 | 188 | for (const auto& val : *vals) 189 | { 190 | // val is an int64_t 191 | } 192 | ``` 193 | 194 | `get_array_of` will return an `option>`, which will be empty if 195 | the key does not exist, is not of the array type, or contains values that 196 | are not of type `T`. 197 | 198 | For nested arrays, it looks like the following: 199 | 200 | ```cpp 201 | auto nested = config->get_array_of("mixed-arr"); 202 | 203 | auto ints = (*nested)[0]->get_array_of(); 204 | // ints is a cpptoml::option> 205 | 206 | auto strings = (*nested)[1]->get_array_of(); 207 | auto doubles = (*nested)[2]->get_array_of(); 208 | ``` 209 | 210 | There is also a `get_qualified_array_of` for simplifying arrays located 211 | inside nested tables. 212 | 213 | ## Arrays of Tables 214 | Suppose you had a configuration file like the following: 215 | 216 | ```toml 217 | [[table-array]] 218 | key1 = "hello" 219 | 220 | [[table-array]] 221 | key1 = "can you hear me" 222 | ``` 223 | 224 | Arrays of tables are represented as a separate type in `cpptoml`. They can 225 | be obtained like so: 226 | 227 | ```cpp 228 | auto config = cpptoml::parse_file("config.toml"); 229 | 230 | auto tarr = config->get_table_array("table-array"); 231 | 232 | for (const auto& table : *tarr) 233 | { 234 | // *table is a cpptoml::table 235 | auto key1 = table->get_as("key1"); 236 | } 237 | ``` 238 | 239 | ## More Examples 240 | You can look at the files files `parse.cpp`, `parse_stdin.cpp`, and 241 | `build_toml.cpp` in the root directory for some more examples. 242 | 243 | `parse_stdin.cpp` shows how to use the visitor pattern to traverse an 244 | entire `cpptoml::table` for serialization. 245 | 246 | `build_toml.cpp` shows how to construct a TOML representation in-memory and 247 | then serialize it to a stream. 248 | 249 | [currver]: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md 250 | [toml]: https://github.com/toml-lang/toml 251 | [toml-test]: https://github.com/BurntSushi/toml-test 252 | [toml-test-fork]: https://github.com/skystrife/toml-test 253 | [ctoml]: https://github.com/evilncrazy/ctoml 254 | [libtoml]: https://github.com/ajwans/libtoml 255 | [tinytoml]: https://github.com/mayah/tinytoml 256 | [boost.toml]: https://github.com/ToruNiina/Boost.toml 257 | -------------------------------------------------------------------------------- /cmake/cpptomlConfig.cmake.in: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/cpptomlTargets.cmake") 2 | -------------------------------------------------------------------------------- /cpptoml.doxygen.in: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.8.1 2 | 3 | # This file describes the settings to be used by the documentation system 4 | # doxygen (www.doxygen.org) for a project. 5 | # 6 | # All text after a hash (#) is considered a comment and will be ignored. 7 | # The format is: 8 | # TAG = value [value, ...] 9 | # For lists items can also be appended using: 10 | # TAG += value [value, ...] 11 | # Values that contain spaces should be placed between quotes (" "). 12 | 13 | #--------------------------------------------------------------------------- 14 | # Project related configuration options 15 | #--------------------------------------------------------------------------- 16 | 17 | # This tag specifies the encoding used for all characters in the config file 18 | # that follow. The default is UTF-8 which is also the encoding used for all 19 | # text before the first occurrence of this tag. Doxygen uses libiconv (or the 20 | # iconv built into libc) for the transcoding. See 21 | # http://www.gnu.org/software/libiconv for the list of possible encodings. 22 | 23 | DOXYFILE_ENCODING = UTF-8 24 | 25 | # The PROJECT_NAME tag is a single word (or sequence of words) that should 26 | # identify the project. Note that if you do not use Doxywizard you need 27 | # to put quotes around the project name if it contains spaces. 28 | 29 | PROJECT_NAME = "cpptoml" 30 | 31 | # The PROJECT_NUMBER tag can be used to enter a project or revision number. 32 | # This could be handy for archiving the generated documentation or 33 | # if some version control system is used. 34 | 35 | PROJECT_NUMBER = 36 | 37 | # Using the PROJECT_BRIEF tag one can provide an optional one line description 38 | # for a project that appears at the top of each page and should give viewer 39 | # a quick idea about the purpose of the project. Keep the description short. 40 | 41 | PROJECT_BRIEF = "A C++11 parser for TOML" 42 | 43 | # With the PROJECT_LOGO tag one can specify an logo or icon that is 44 | # included in the documentation. The maximum height of the logo should not 45 | # exceed 55 pixels and the maximum width should not exceed 200 pixels. 46 | # Doxygen will copy the logo to the output directory. 47 | 48 | PROJECT_LOGO = 49 | 50 | # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 51 | # base path where the generated documentation will be put. 52 | # If a relative path is entered, it will be relative to the location 53 | # where doxygen was started. If left blank the current directory will be used. 54 | 55 | OUTPUT_DIRECTORY = ./doc 56 | 57 | # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 58 | # 4096 sub-directories (in 2 levels) under the output directory of each output 59 | # format and will distribute the generated files over these directories. 60 | # Enabling this option can be useful when feeding doxygen a huge amount of 61 | # source files, where putting all generated files in the same directory would 62 | # otherwise cause performance problems for the file system. 63 | 64 | CREATE_SUBDIRS = NO 65 | 66 | # The OUTPUT_LANGUAGE tag is used to specify the language in which all 67 | # documentation generated by doxygen is written. Doxygen will use this 68 | # information to generate all constant output in the proper language. 69 | # The default language is English, other supported languages are: 70 | # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 71 | # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, 72 | # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English 73 | # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, 74 | # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, 75 | # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. 76 | 77 | OUTPUT_LANGUAGE = English 78 | 79 | # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 80 | # include brief member descriptions after the members that are listed in 81 | # the file and class documentation (similar to JavaDoc). 82 | # Set to NO to disable this. 83 | 84 | BRIEF_MEMBER_DESC = YES 85 | 86 | # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 87 | # the brief description of a member or function before the detailed description. 88 | # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 89 | # brief descriptions will be completely suppressed. 90 | 91 | REPEAT_BRIEF = YES 92 | 93 | # This tag implements a quasi-intelligent brief description abbreviator 94 | # that is used to form the text in various listings. Each string 95 | # in this list, if found as the leading text of the brief description, will be 96 | # stripped from the text and the result after processing the whole list, is 97 | # used as the annotated text. Otherwise, the brief description is used as-is. 98 | # If left blank, the following values are used ("$name" is automatically 99 | # replaced with the name of the entity): "The $name class" "The $name widget" 100 | # "The $name file" "is" "provides" "specifies" "contains" 101 | # "represents" "a" "an" "the" 102 | 103 | ABBREVIATE_BRIEF = 104 | 105 | # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 106 | # Doxygen will generate a detailed section even if there is only a brief 107 | # description. 108 | 109 | ALWAYS_DETAILED_SEC = NO 110 | 111 | # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 112 | # inherited members of a class in the documentation of that class as if those 113 | # members were ordinary class members. Constructors, destructors and assignment 114 | # operators of the base classes will not be shown. 115 | 116 | INLINE_INHERITED_MEMB = NO 117 | 118 | # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 119 | # path before files name in the file list and in the header files. If set 120 | # to NO the shortest path that makes the file name unique will be used. 121 | 122 | FULL_PATH_NAMES = YES 123 | 124 | # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 125 | # can be used to strip a user-defined part of the path. Stripping is 126 | # only done if one of the specified strings matches the left-hand part of 127 | # the path. The tag can be used to show relative paths in the file list. 128 | # If left blank the directory from which doxygen is run is used as the 129 | # path to strip. 130 | 131 | STRIP_FROM_PATH = 132 | 133 | # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 134 | # the path mentioned in the documentation of a class, which tells 135 | # the reader which header file to include in order to use a class. 136 | # If left blank only the name of the header file containing the class 137 | # definition is used. Otherwise one should specify the include paths that 138 | # are normally passed to the compiler using the -I flag. 139 | 140 | STRIP_FROM_INC_PATH = 141 | 142 | # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 143 | # (but less readable) file names. This can be useful if your file system 144 | # doesn't support long names like on DOS, Mac, or CD-ROM. 145 | 146 | SHORT_NAMES = NO 147 | 148 | # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 149 | # will interpret the first line (until the first dot) of a JavaDoc-style 150 | # comment as the brief description. If set to NO, the JavaDoc 151 | # comments will behave just like regular Qt-style comments 152 | # (thus requiring an explicit @brief command for a brief description.) 153 | 154 | JAVADOC_AUTOBRIEF = YES 155 | 156 | # If the QT_AUTOBRIEF tag is set to YES then Doxygen will 157 | # interpret the first line (until the first dot) of a Qt-style 158 | # comment as the brief description. If set to NO, the comments 159 | # will behave just like regular Qt-style comments (thus requiring 160 | # an explicit \brief command for a brief description.) 161 | 162 | QT_AUTOBRIEF = NO 163 | 164 | # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 165 | # treat a multi-line C++ special comment block (i.e. a block of //! or /// 166 | # comments) as a brief description. This used to be the default behaviour. 167 | # The new default is to treat a multi-line C++ comment block as a detailed 168 | # description. Set this tag to YES if you prefer the old behaviour instead. 169 | 170 | MULTILINE_CPP_IS_BRIEF = NO 171 | 172 | # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 173 | # member inherits the documentation from any documented member that it 174 | # re-implements. 175 | 176 | INHERIT_DOCS = YES 177 | 178 | # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 179 | # a new page for each member. If set to NO, the documentation of a member will 180 | # be part of the file/class/namespace that contains it. 181 | 182 | SEPARATE_MEMBER_PAGES = NO 183 | 184 | # The TAB_SIZE tag can be used to set the number of spaces in a tab. 185 | # Doxygen uses this value to replace tabs by spaces in code fragments. 186 | 187 | TAB_SIZE = 4 188 | 189 | # This tag can be used to specify a number of aliases that acts 190 | # as commands in the documentation. An alias has the form "name=value". 191 | # For example adding "sideeffect=\par Side Effects:\n" will allow you to 192 | # put the command \sideeffect (or @sideeffect) in the documentation, which 193 | # will result in a user-defined paragraph with heading "Side Effects:". 194 | # You can put \n's in the value part of an alias to insert newlines. 195 | 196 | ALIASES = 197 | 198 | # This tag can be used to specify a number of word-keyword mappings (TCL only). 199 | # A mapping has the form "name=value". For example adding 200 | # "class=itcl::class" will allow you to use the command class in the 201 | # itcl::class meaning. 202 | 203 | TCL_SUBST = 204 | 205 | # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 206 | # sources only. Doxygen will then generate output that is more tailored for C. 207 | # For instance, some of the names that are used will be different. The list 208 | # of all members will be omitted, etc. 209 | 210 | OPTIMIZE_OUTPUT_FOR_C = NO 211 | 212 | # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 213 | # sources only. Doxygen will then generate output that is more tailored for 214 | # Java. For instance, namespaces will be presented as packages, qualified 215 | # scopes will look different, etc. 216 | 217 | OPTIMIZE_OUTPUT_JAVA = NO 218 | 219 | # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 220 | # sources only. Doxygen will then generate output that is more tailored for 221 | # Fortran. 222 | 223 | OPTIMIZE_FOR_FORTRAN = NO 224 | 225 | # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 226 | # sources. Doxygen will then generate output that is tailored for 227 | # VHDL. 228 | 229 | OPTIMIZE_OUTPUT_VHDL = NO 230 | 231 | # Doxygen selects the parser to use depending on the extension of the files it 232 | # parses. With this tag you can assign which parser to use for a given extension. 233 | # Doxygen has a built-in mapping, but you can override or extend it using this 234 | # tag. The format is ext=language, where ext is a file extension, and language 235 | # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, 236 | # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make 237 | # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C 238 | # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions 239 | # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. 240 | 241 | EXTENSION_MAPPING = tcc=C++ 242 | 243 | # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all 244 | # comments according to the Markdown format, which allows for more readable 245 | # documentation. See http://daringfireball.net/projects/markdown/ for details. 246 | # The output of markdown processing is further processed by doxygen, so you 247 | # can mix doxygen, HTML, and XML commands with Markdown formatting. 248 | # Disable only in case of backward compatibilities issues. 249 | 250 | MARKDOWN_SUPPORT = YES 251 | 252 | # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 253 | # to include (a tag file for) the STL sources as input, then you should 254 | # set this tag to YES in order to let doxygen match functions declarations and 255 | # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 256 | # func(std::string) {}). This also makes the inheritance and collaboration 257 | # diagrams that involve STL classes more complete and accurate. 258 | 259 | BUILTIN_STL_SUPPORT = YES 260 | 261 | # If you use Microsoft's C++/CLI language, you should set this option to YES to 262 | # enable parsing support. 263 | 264 | CPP_CLI_SUPPORT = NO 265 | 266 | # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 267 | # Doxygen will parse them like normal C++ but will assume all classes use public 268 | # instead of private inheritance when no explicit protection keyword is present. 269 | 270 | SIP_SUPPORT = NO 271 | 272 | # For Microsoft's IDL there are propget and propput attributes to indicate getter 273 | # and setter methods for a property. Setting this option to YES (the default) 274 | # will make doxygen replace the get and set methods by a property in the 275 | # documentation. This will only work if the methods are indeed getting or 276 | # setting a simple type. If this is not the case, or you want to show the 277 | # methods anyway, you should set this option to NO. 278 | 279 | IDL_PROPERTY_SUPPORT = YES 280 | 281 | # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 282 | # tag is set to YES, then doxygen will reuse the documentation of the first 283 | # member in the group (if any) for the other members of the group. By default 284 | # all members of a group must be documented explicitly. 285 | 286 | DISTRIBUTE_GROUP_DOC = NO 287 | 288 | # Set the SUBGROUPING tag to YES (the default) to allow class member groups of 289 | # the same type (for instance a group of public functions) to be put as a 290 | # subgroup of that type (e.g. under the Public Functions section). Set it to 291 | # NO to prevent subgrouping. Alternatively, this can be done per class using 292 | # the \nosubgrouping command. 293 | 294 | SUBGROUPING = YES 295 | 296 | # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and 297 | # unions are shown inside the group in which they are included (e.g. using 298 | # @ingroup) instead of on a separate page (for HTML and Man pages) or 299 | # section (for LaTeX and RTF). 300 | 301 | INLINE_GROUPED_CLASSES = NO 302 | 303 | # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and 304 | # unions with only public data fields will be shown inline in the documentation 305 | # of the scope in which they are defined (i.e. file, namespace, or group 306 | # documentation), provided this scope is documented. If set to NO (the default), 307 | # structs, classes, and unions are shown on a separate page (for HTML and Man 308 | # pages) or section (for LaTeX and RTF). 309 | 310 | INLINE_SIMPLE_STRUCTS = NO 311 | 312 | # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 313 | # is documented as struct, union, or enum with the name of the typedef. So 314 | # typedef struct TypeS {} TypeT, will appear in the documentation as a struct 315 | # with name TypeT. When disabled the typedef will appear as a member of a file, 316 | # namespace, or class. And the struct will be named TypeS. This can typically 317 | # be useful for C code in case the coding convention dictates that all compound 318 | # types are typedef'ed and only the typedef is referenced, never the tag name. 319 | 320 | TYPEDEF_HIDES_STRUCT = NO 321 | 322 | # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 323 | # determine which symbols to keep in memory and which to flush to disk. 324 | # When the cache is full, less often used symbols will be written to disk. 325 | # For small to medium size projects (<1000 input files) the default value is 326 | # probably good enough. For larger projects a too small cache size can cause 327 | # doxygen to be busy swapping symbols to and from disk most of the time 328 | # causing a significant performance penalty. 329 | # If the system has enough physical memory increasing the cache will improve the 330 | # performance by keeping more symbols in memory. Note that the value works on 331 | # a logarithmic scale so increasing the size by one will roughly double the 332 | # memory usage. The cache size is given by this formula: 333 | # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 334 | # corresponding to a cache size of 2^16 = 65536 symbols. 335 | 336 | SYMBOL_CACHE_SIZE = 0 337 | 338 | # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be 339 | # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given 340 | # their name and scope. Since this can be an expensive process and often the 341 | # same symbol appear multiple times in the code, doxygen keeps a cache of 342 | # pre-resolved symbols. If the cache is too small doxygen will become slower. 343 | # If the cache is too large, memory is wasted. The cache size is given by this 344 | # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, 345 | # corresponding to a cache size of 2^16 = 65536 symbols. 346 | 347 | LOOKUP_CACHE_SIZE = 0 348 | 349 | #--------------------------------------------------------------------------- 350 | # Build related configuration options 351 | #--------------------------------------------------------------------------- 352 | 353 | # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 354 | # documentation are documented, even if no documentation was available. 355 | # Private class members and static file members will be hidden unless 356 | # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES 357 | 358 | EXTRACT_ALL = NO 359 | 360 | # If the EXTRACT_PRIVATE tag is set to YES all private members of a class 361 | # will be included in the documentation. 362 | 363 | EXTRACT_PRIVATE = YES 364 | 365 | # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. 366 | 367 | EXTRACT_PACKAGE = NO 368 | 369 | # If the EXTRACT_STATIC tag is set to YES all static members of a file 370 | # will be included in the documentation. 371 | 372 | EXTRACT_STATIC = NO 373 | 374 | # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 375 | # defined locally in source files will be included in the documentation. 376 | # If set to NO only classes defined in header files are included. 377 | 378 | EXTRACT_LOCAL_CLASSES = YES 379 | 380 | # This flag is only useful for Objective-C code. When set to YES local 381 | # methods, which are defined in the implementation section but not in 382 | # the interface are included in the documentation. 383 | # If set to NO (the default) only methods in the interface are included. 384 | 385 | EXTRACT_LOCAL_METHODS = NO 386 | 387 | # If this flag is set to YES, the members of anonymous namespaces will be 388 | # extracted and appear in the documentation as a namespace called 389 | # 'anonymous_namespace{file}', where file will be replaced with the base 390 | # name of the file that contains the anonymous namespace. By default 391 | # anonymous namespaces are hidden. 392 | 393 | EXTRACT_ANON_NSPACES = NO 394 | 395 | # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 396 | # undocumented members of documented classes, files or namespaces. 397 | # If set to NO (the default) these members will be included in the 398 | # various overviews, but no documentation section is generated. 399 | # This option has no effect if EXTRACT_ALL is enabled. 400 | 401 | HIDE_UNDOC_MEMBERS = NO 402 | 403 | # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 404 | # undocumented classes that are normally visible in the class hierarchy. 405 | # If set to NO (the default) these classes will be included in the various 406 | # overviews. This option has no effect if EXTRACT_ALL is enabled. 407 | 408 | HIDE_UNDOC_CLASSES = NO 409 | 410 | # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 411 | # friend (class|struct|union) declarations. 412 | # If set to NO (the default) these declarations will be included in the 413 | # documentation. 414 | 415 | HIDE_FRIEND_COMPOUNDS = NO 416 | 417 | # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 418 | # documentation blocks found inside the body of a function. 419 | # If set to NO (the default) these blocks will be appended to the 420 | # function's detailed documentation block. 421 | 422 | HIDE_IN_BODY_DOCS = NO 423 | 424 | # The INTERNAL_DOCS tag determines if documentation 425 | # that is typed after a \internal command is included. If the tag is set 426 | # to NO (the default) then the documentation will be excluded. 427 | # Set it to YES to include the internal documentation. 428 | 429 | INTERNAL_DOCS = NO 430 | 431 | # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 432 | # file names in lower-case letters. If set to YES upper-case letters are also 433 | # allowed. This is useful if you have classes or files whose names only differ 434 | # in case and if your file system supports case sensitive file names. Windows 435 | # and Mac users are advised to set this option to NO. 436 | 437 | CASE_SENSE_NAMES = YES 438 | 439 | # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 440 | # will show members with their full class and namespace scopes in the 441 | # documentation. If set to YES the scope will be hidden. 442 | 443 | HIDE_SCOPE_NAMES = NO 444 | 445 | # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 446 | # will put a list of the files that are included by a file in the documentation 447 | # of that file. 448 | 449 | SHOW_INCLUDE_FILES = YES 450 | 451 | # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen 452 | # will list include files with double quotes in the documentation 453 | # rather than with sharp brackets. 454 | 455 | FORCE_LOCAL_INCLUDES = NO 456 | 457 | # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 458 | # is inserted in the documentation for inline members. 459 | 460 | INLINE_INFO = YES 461 | 462 | # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 463 | # will sort the (detailed) documentation of file and class members 464 | # alphabetically by member name. If set to NO the members will appear in 465 | # declaration order. 466 | 467 | SORT_MEMBER_DOCS = NO 468 | 469 | # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 470 | # brief documentation of file, namespace and class members alphabetically 471 | # by member name. If set to NO (the default) the members will appear in 472 | # declaration order. 473 | 474 | SORT_BRIEF_DOCS = NO 475 | 476 | # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen 477 | # will sort the (brief and detailed) documentation of class members so that 478 | # constructors and destructors are listed first. If set to NO (the default) 479 | # the constructors will appear in the respective orders defined by 480 | # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. 481 | # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO 482 | # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. 483 | 484 | SORT_MEMBERS_CTORS_1ST = NO 485 | 486 | # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 487 | # hierarchy of group names into alphabetical order. If set to NO (the default) 488 | # the group names will appear in their defined order. 489 | 490 | SORT_GROUP_NAMES = NO 491 | 492 | # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 493 | # sorted by fully-qualified names, including namespaces. If set to 494 | # NO (the default), the class list will be sorted only by class name, 495 | # not including the namespace part. 496 | # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 497 | # Note: This option applies only to the class list, not to the 498 | # alphabetical list. 499 | 500 | SORT_BY_SCOPE_NAME = NO 501 | 502 | # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to 503 | # do proper type resolution of all parameters of a function it will reject a 504 | # match between the prototype and the implementation of a member function even 505 | # if there is only one candidate or it is obvious which candidate to choose 506 | # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen 507 | # will still accept a match between prototype and implementation in such cases. 508 | 509 | STRICT_PROTO_MATCHING = NO 510 | 511 | # The GENERATE_TODOLIST tag can be used to enable (YES) or 512 | # disable (NO) the todo list. This list is created by putting \todo 513 | # commands in the documentation. 514 | 515 | GENERATE_TODOLIST = YES 516 | 517 | # The GENERATE_TESTLIST tag can be used to enable (YES) or 518 | # disable (NO) the test list. This list is created by putting \test 519 | # commands in the documentation. 520 | 521 | GENERATE_TESTLIST = YES 522 | 523 | # The GENERATE_BUGLIST tag can be used to enable (YES) or 524 | # disable (NO) the bug list. This list is created by putting \bug 525 | # commands in the documentation. 526 | 527 | GENERATE_BUGLIST = YES 528 | 529 | # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 530 | # disable (NO) the deprecated list. This list is created by putting 531 | # \deprecated commands in the documentation. 532 | 533 | GENERATE_DEPRECATEDLIST= YES 534 | 535 | # The ENABLED_SECTIONS tag can be used to enable conditional 536 | # documentation sections, marked by \if sectionname ... \endif. 537 | 538 | ENABLED_SECTIONS = 539 | 540 | # The MAX_INITIALIZER_LINES tag determines the maximum number of lines 541 | # the initial value of a variable or macro consists of for it to appear in 542 | # the documentation. If the initializer consists of more lines than specified 543 | # here it will be hidden. Use a value of 0 to hide initializers completely. 544 | # The appearance of the initializer of individual variables and macros in the 545 | # documentation can be controlled using \showinitializer or \hideinitializer 546 | # command in the documentation regardless of this setting. 547 | 548 | MAX_INITIALIZER_LINES = 30 549 | 550 | # Set the SHOW_USED_FILES tag to NO to disable the list of files generated 551 | # at the bottom of the documentation of classes and structs. If set to YES the 552 | # list will mention the files that were used to generate the documentation. 553 | 554 | SHOW_USED_FILES = YES 555 | 556 | # Set the SHOW_FILES tag to NO to disable the generation of the Files page. 557 | # This will remove the Files entry from the Quick Index and from the 558 | # Folder Tree View (if specified). The default is YES. 559 | 560 | SHOW_FILES = YES 561 | 562 | # Set the SHOW_NAMESPACES tag to NO to disable the generation of the 563 | # Namespaces page. 564 | # This will remove the Namespaces entry from the Quick Index 565 | # and from the Folder Tree View (if specified). The default is YES. 566 | 567 | SHOW_NAMESPACES = YES 568 | 569 | # The FILE_VERSION_FILTER tag can be used to specify a program or script that 570 | # doxygen should invoke to get the current version for each file (typically from 571 | # the version control system). Doxygen will invoke the program by executing (via 572 | # popen()) the command , where is the value of 573 | # the FILE_VERSION_FILTER tag, and is the name of an input file 574 | # provided by doxygen. Whatever the program writes to standard output 575 | # is used as the file version. See the manual for examples. 576 | 577 | FILE_VERSION_FILTER = 578 | 579 | # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed 580 | # by doxygen. The layout file controls the global structure of the generated 581 | # output files in an output format independent way. The create the layout file 582 | # that represents doxygen's defaults, run doxygen with the -l option. 583 | # You can optionally specify a file name after the option, if omitted 584 | # DoxygenLayout.xml will be used as the name of the layout file. 585 | 586 | LAYOUT_FILE = 587 | 588 | # The CITE_BIB_FILES tag can be used to specify one or more bib files 589 | # containing the references data. This must be a list of .bib files. The 590 | # .bib extension is automatically appended if omitted. Using this command 591 | # requires the bibtex tool to be installed. See also 592 | # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style 593 | # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this 594 | # feature you need bibtex and perl available in the search path. 595 | 596 | CITE_BIB_FILES = 597 | 598 | #--------------------------------------------------------------------------- 599 | # configuration options related to warning and progress messages 600 | #--------------------------------------------------------------------------- 601 | 602 | # The QUIET tag can be used to turn on/off the messages that are generated 603 | # by doxygen. Possible values are YES and NO. If left blank NO is used. 604 | 605 | QUIET = NO 606 | 607 | # The WARNINGS tag can be used to turn on/off the warning messages that are 608 | # generated by doxygen. Possible values are YES and NO. If left blank 609 | # NO is used. 610 | 611 | WARNINGS = YES 612 | 613 | # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 614 | # for undocumented members. If EXTRACT_ALL is set to YES then this flag will 615 | # automatically be disabled. 616 | 617 | WARN_IF_UNDOCUMENTED = YES 618 | 619 | # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 620 | # potential errors in the documentation, such as not documenting some 621 | # parameters in a documented function, or documenting parameters that 622 | # don't exist or using markup commands wrongly. 623 | 624 | WARN_IF_DOC_ERROR = YES 625 | 626 | # The WARN_NO_PARAMDOC option can be enabled to get warnings for 627 | # functions that are documented, but have no documentation for their parameters 628 | # or return value. If set to NO (the default) doxygen will only warn about 629 | # wrong or incomplete parameter documentation, but not about the absence of 630 | # documentation. 631 | 632 | WARN_NO_PARAMDOC = NO 633 | 634 | # The WARN_FORMAT tag determines the format of the warning messages that 635 | # doxygen can produce. The string should contain the $file, $line, and $text 636 | # tags, which will be replaced by the file and line number from which the 637 | # warning originated and the warning text. Optionally the format may contain 638 | # $version, which will be replaced by the version of the file (if it could 639 | # be obtained via FILE_VERSION_FILTER) 640 | 641 | WARN_FORMAT = "$file:$line: $text" 642 | 643 | # The WARN_LOGFILE tag can be used to specify a file to which warning 644 | # and error messages should be written. If left blank the output is written 645 | # to stderr. 646 | 647 | WARN_LOGFILE = 648 | 649 | #--------------------------------------------------------------------------- 650 | # configuration options related to the input files 651 | #--------------------------------------------------------------------------- 652 | 653 | # The INPUT tag can be used to specify the files and/or directories that contain 654 | # documented source files. You may enter file names like "myfile.cpp" or 655 | # directories like "/usr/src/myproject". Separate the files or directories 656 | # with spaces. 657 | 658 | INPUT = @CMAKE_CURRENT_SOURCE_DIR@ 659 | 660 | # This tag can be used to specify the character encoding of the source files 661 | # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 662 | # also the default input encoding. Doxygen uses libiconv (or the iconv built 663 | # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 664 | # the list of possible encodings. 665 | 666 | INPUT_ENCODING = UTF-8 667 | 668 | # If the value of the INPUT tag contains directories, you can use the 669 | # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 670 | # and *.h) to filter out the source-files in the directories. If left 671 | # blank the following patterns are tested: 672 | # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh 673 | # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py 674 | # *.f90 *.f *.for *.vhd *.vhdl 675 | 676 | FILE_PATTERNS = *.c *.cpp *.h *.rb *.py *.java *.tcc 677 | 678 | # The RECURSIVE tag can be used to turn specify whether or not subdirectories 679 | # should be searched for input files as well. Possible values are YES and NO. 680 | # If left blank NO is used. 681 | 682 | RECURSIVE = YES 683 | 684 | # The EXCLUDE tag can be used to specify files and/or directories that should be 685 | # excluded from the INPUT source files. This way you can easily exclude a 686 | # subdirectory from a directory tree whose root is specified with the INPUT tag. 687 | # Note that relative paths are relative to the directory from which doxygen is 688 | # run. 689 | 690 | EXCLUDE = "lib" 691 | 692 | # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or 693 | # directories that are symbolic links (a Unix file system feature) are excluded 694 | # from the input. 695 | 696 | EXCLUDE_SYMLINKS = NO 697 | 698 | # If the value of the INPUT tag contains directories, you can use the 699 | # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 700 | # certain files from those directories. Note that the wildcards are matched 701 | # against the file with absolute path, so to exclude all test directories 702 | # for example use the pattern */test/* 703 | 704 | EXCLUDE_PATTERNS = 705 | 706 | # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 707 | # (namespaces, classes, functions, etc.) that should be excluded from the 708 | # output. The symbol name can be a fully qualified name, a word, or if the 709 | # wildcard * is used, a substring. Examples: ANamespace, AClass, 710 | # AClass::ANamespace, ANamespace::*Test 711 | 712 | EXCLUDE_SYMBOLS = 713 | 714 | # The EXAMPLE_PATH tag can be used to specify one or more files or 715 | # directories that contain example code fragments that are included (see 716 | # the \include command). 717 | 718 | EXAMPLE_PATH = 719 | 720 | # If the value of the EXAMPLE_PATH tag contains directories, you can use the 721 | # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 722 | # and *.h) to filter out the source-files in the directories. If left 723 | # blank all files are included. 724 | 725 | EXAMPLE_PATTERNS = 726 | 727 | # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 728 | # searched for input files to be used with the \include or \dontinclude 729 | # commands irrespective of the value of the RECURSIVE tag. 730 | # Possible values are YES and NO. If left blank NO is used. 731 | 732 | EXAMPLE_RECURSIVE = NO 733 | 734 | # The IMAGE_PATH tag can be used to specify one or more files or 735 | # directories that contain image that are included in the documentation (see 736 | # the \image command). 737 | 738 | IMAGE_PATH = 739 | 740 | # The INPUT_FILTER tag can be used to specify a program that doxygen should 741 | # invoke to filter for each input file. Doxygen will invoke the filter program 742 | # by executing (via popen()) the command , where 743 | # is the value of the INPUT_FILTER tag, and is the name of an 744 | # input file. Doxygen will then use the output that the filter program writes 745 | # to standard output. 746 | # If FILTER_PATTERNS is specified, this tag will be 747 | # ignored. 748 | 749 | INPUT_FILTER = 750 | 751 | # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 752 | # basis. 753 | # Doxygen will compare the file name with each pattern and apply the 754 | # filter if there is a match. 755 | # The filters are a list of the form: 756 | # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 757 | # info on how filters are used. If FILTER_PATTERNS is empty or if 758 | # non of the patterns match the file name, INPUT_FILTER is applied. 759 | 760 | FILTER_PATTERNS = 761 | 762 | # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 763 | # INPUT_FILTER) will be used to filter the input files when producing source 764 | # files to browse (i.e. when SOURCE_BROWSER is set to YES). 765 | 766 | FILTER_SOURCE_FILES = NO 767 | 768 | # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file 769 | # pattern. A pattern will override the setting for FILTER_PATTERN (if any) 770 | # and it is also possible to disable source filtering for a specific pattern 771 | # using *.ext= (so without naming a filter). This option only has effect when 772 | # FILTER_SOURCE_FILES is enabled. 773 | 774 | FILTER_SOURCE_PATTERNS = 775 | 776 | #--------------------------------------------------------------------------- 777 | # configuration options related to source browsing 778 | #--------------------------------------------------------------------------- 779 | 780 | # If the SOURCE_BROWSER tag is set to YES then a list of source files will 781 | # be generated. Documented entities will be cross-referenced with these sources. 782 | # Note: To get rid of all source code in the generated output, make sure also 783 | # VERBATIM_HEADERS is set to NO. 784 | 785 | SOURCE_BROWSER = NO 786 | 787 | # Setting the INLINE_SOURCES tag to YES will include the body 788 | # of functions and classes directly in the documentation. 789 | 790 | INLINE_SOURCES = NO 791 | 792 | # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 793 | # doxygen to hide any special comment blocks from generated source code 794 | # fragments. Normal C and C++ comments will always remain visible. 795 | 796 | STRIP_CODE_COMMENTS = YES 797 | 798 | # If the REFERENCED_BY_RELATION tag is set to YES 799 | # then for each documented function all documented 800 | # functions referencing it will be listed. 801 | 802 | REFERENCED_BY_RELATION = NO 803 | 804 | # If the REFERENCES_RELATION tag is set to YES 805 | # then for each documented function all documented entities 806 | # called/used by that function will be listed. 807 | 808 | REFERENCES_RELATION = NO 809 | 810 | # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 811 | # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 812 | # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 813 | # link to the source code. 814 | # Otherwise they will link to the documentation. 815 | 816 | REFERENCES_LINK_SOURCE = YES 817 | 818 | # If the USE_HTAGS tag is set to YES then the references to source code 819 | # will point to the HTML generated by the htags(1) tool instead of doxygen 820 | # built-in source browser. The htags tool is part of GNU's global source 821 | # tagging system (see http://www.gnu.org/software/global/global.html). You 822 | # will need version 4.8.6 or higher. 823 | 824 | USE_HTAGS = NO 825 | 826 | # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 827 | # will generate a verbatim copy of the header file for each class for 828 | # which an include is specified. Set to NO to disable this. 829 | 830 | VERBATIM_HEADERS = YES 831 | 832 | #--------------------------------------------------------------------------- 833 | # configuration options related to the alphabetical class index 834 | #--------------------------------------------------------------------------- 835 | 836 | # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 837 | # of all compounds will be generated. Enable this if the project 838 | # contains a lot of classes, structs, unions or interfaces. 839 | 840 | ALPHABETICAL_INDEX = YES 841 | 842 | # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 843 | # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 844 | # in which this list will be split (can be a number in the range [1..20]) 845 | 846 | COLS_IN_ALPHA_INDEX = 5 847 | 848 | # In case all classes in a project start with a common prefix, all 849 | # classes will be put under the same header in the alphabetical index. 850 | # The IGNORE_PREFIX tag can be used to specify one or more prefixes that 851 | # should be ignored while generating the index headers. 852 | 853 | IGNORE_PREFIX = 854 | 855 | #--------------------------------------------------------------------------- 856 | # configuration options related to the HTML output 857 | #--------------------------------------------------------------------------- 858 | 859 | # If the GENERATE_HTML tag is set to YES (the default) Doxygen will 860 | # generate HTML output. 861 | 862 | GENERATE_HTML = YES 863 | 864 | # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 865 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 866 | # put in front of it. If left blank `html' will be used as the default path. 867 | 868 | HTML_OUTPUT = html 869 | 870 | # The HTML_FILE_EXTENSION tag can be used to specify the file extension for 871 | # each generated HTML page (for example: .htm,.php,.asp). If it is left blank 872 | # doxygen will generate files with .html extension. 873 | 874 | HTML_FILE_EXTENSION = .html 875 | 876 | # The HTML_HEADER tag can be used to specify a personal HTML header for 877 | # each generated HTML page. If it is left blank doxygen will generate a 878 | # standard header. Note that when using a custom header you are responsible 879 | # for the proper inclusion of any scripts and style sheets that doxygen 880 | # needs, which is dependent on the configuration options used. 881 | # It is advised to generate a default header using "doxygen -w html 882 | # header.html footer.html stylesheet.css YourConfigFile" and then modify 883 | # that header. Note that the header is subject to change so you typically 884 | # have to redo this when upgrading to a newer version of doxygen or when 885 | # changing the value of configuration settings such as GENERATE_TREEVIEW! 886 | 887 | HTML_HEADER = 888 | 889 | # The HTML_FOOTER tag can be used to specify a personal HTML footer for 890 | # each generated HTML page. If it is left blank doxygen will generate a 891 | # standard footer. 892 | 893 | HTML_FOOTER = 894 | 895 | # The HTML_STYLESHEET tag can be used to specify a user-defined cascading 896 | # style sheet that is used by each HTML page. It can be used to 897 | # fine-tune the look of the HTML output. If the tag is left blank doxygen 898 | # will generate a default style sheet. Note that doxygen will try to copy 899 | # the style sheet file to the HTML output directory, so don't put your own 900 | # style sheet in the HTML output directory as well, or it will be erased! 901 | 902 | HTML_STYLESHEET = 903 | 904 | # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or 905 | # other source files which should be copied to the HTML output directory. Note 906 | # that these files will be copied to the base HTML output directory. Use the 907 | # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these 908 | # files. In the HTML_STYLESHEET file, use the file name only. Also note that 909 | # the files will be copied as-is; there are no commands or markers available. 910 | 911 | HTML_EXTRA_FILES = 912 | 913 | # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. 914 | # Doxygen will adjust the colors in the style sheet and background images 915 | # according to this color. Hue is specified as an angle on a colorwheel, 916 | # see http://en.wikipedia.org/wiki/Hue for more information. 917 | # For instance the value 0 represents red, 60 is yellow, 120 is green, 918 | # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. 919 | # The allowed range is 0 to 359. 920 | 921 | HTML_COLORSTYLE_HUE = 220 922 | 923 | # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of 924 | # the colors in the HTML output. For a value of 0 the output will use 925 | # grayscales only. A value of 255 will produce the most vivid colors. 926 | 927 | HTML_COLORSTYLE_SAT = 100 928 | 929 | # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to 930 | # the luminance component of the colors in the HTML output. Values below 931 | # 100 gradually make the output lighter, whereas values above 100 make 932 | # the output darker. The value divided by 100 is the actual gamma applied, 933 | # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, 934 | # and 100 does not change the gamma. 935 | 936 | HTML_COLORSTYLE_GAMMA = 80 937 | 938 | # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML 939 | # page will contain the date and time when the page was generated. Setting 940 | # this to NO can help when comparing the output of multiple runs. 941 | 942 | HTML_TIMESTAMP = YES 943 | 944 | # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 945 | # documentation will contain sections that can be hidden and shown after the 946 | # page has loaded. For this to work a browser that supports 947 | # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 948 | # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). 949 | 950 | HTML_DYNAMIC_SECTIONS = NO 951 | 952 | # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of 953 | # entries shown in the various tree structured indices initially; the user 954 | # can expand and collapse entries dynamically later on. Doxygen will expand 955 | # the tree to such a level that at most the specified number of entries are 956 | # visible (unless a fully collapsed tree already exceeds this amount). 957 | # So setting the number of entries 1 will produce a full collapsed tree by 958 | # default. 0 is a special value representing an infinite number of entries 959 | # and will result in a full expanded tree by default. 960 | 961 | HTML_INDEX_NUM_ENTRIES = 100 962 | 963 | # If the GENERATE_DOCSET tag is set to YES, additional index files 964 | # will be generated that can be used as input for Apple's Xcode 3 965 | # integrated development environment, introduced with OSX 10.5 (Leopard). 966 | # To create a documentation set, doxygen will generate a Makefile in the 967 | # HTML output directory. Running make will produce the docset in that 968 | # directory and running "make install" will install the docset in 969 | # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 970 | # it at startup. 971 | # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html 972 | # for more information. 973 | 974 | GENERATE_DOCSET = NO 975 | 976 | # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 977 | # feed. A documentation feed provides an umbrella under which multiple 978 | # documentation sets from a single provider (such as a company or product suite) 979 | # can be grouped. 980 | 981 | DOCSET_FEEDNAME = "Doxygen generated docs" 982 | 983 | # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 984 | # should uniquely identify the documentation set bundle. This should be a 985 | # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 986 | # will append .docset to the name. 987 | 988 | DOCSET_BUNDLE_ID = org.doxygen.Project 989 | 990 | # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify 991 | # the documentation publisher. This should be a reverse domain-name style 992 | # string, e.g. com.mycompany.MyDocSet.documentation. 993 | 994 | DOCSET_PUBLISHER_ID = org.doxygen.Publisher 995 | 996 | # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. 997 | 998 | DOCSET_PUBLISHER_NAME = Publisher 999 | 1000 | # If the GENERATE_HTMLHELP tag is set to YES, additional index files 1001 | # will be generated that can be used as input for tools like the 1002 | # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 1003 | # of the generated HTML documentation. 1004 | 1005 | GENERATE_HTMLHELP = NO 1006 | 1007 | # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 1008 | # be used to specify the file name of the resulting .chm file. You 1009 | # can add a path in front of the file if the result should not be 1010 | # written to the html output directory. 1011 | 1012 | CHM_FILE = 1013 | 1014 | # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 1015 | # be used to specify the location (absolute path including file name) of 1016 | # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 1017 | # the HTML help compiler on the generated index.hhp. 1018 | 1019 | HHC_LOCATION = 1020 | 1021 | # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 1022 | # controls if a separate .chi index file is generated (YES) or that 1023 | # it should be included in the master .chm file (NO). 1024 | 1025 | GENERATE_CHI = NO 1026 | 1027 | # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING 1028 | # is used to encode HtmlHelp index (hhk), content (hhc) and project file 1029 | # content. 1030 | 1031 | CHM_INDEX_ENCODING = 1032 | 1033 | # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 1034 | # controls whether a binary table of contents is generated (YES) or a 1035 | # normal table of contents (NO) in the .chm file. 1036 | 1037 | BINARY_TOC = NO 1038 | 1039 | # The TOC_EXPAND flag can be set to YES to add extra items for group members 1040 | # to the contents of the HTML help documentation and to the tree view. 1041 | 1042 | TOC_EXPAND = NO 1043 | 1044 | # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and 1045 | # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated 1046 | # that can be used as input for Qt's qhelpgenerator to generate a 1047 | # Qt Compressed Help (.qch) of the generated HTML documentation. 1048 | 1049 | GENERATE_QHP = NO 1050 | 1051 | # If the QHG_LOCATION tag is specified, the QCH_FILE tag can 1052 | # be used to specify the file name of the resulting .qch file. 1053 | # The path specified is relative to the HTML output folder. 1054 | 1055 | QCH_FILE = 1056 | 1057 | # The QHP_NAMESPACE tag specifies the namespace to use when generating 1058 | # Qt Help Project output. For more information please see 1059 | # http://doc.trolltech.com/qthelpproject.html#namespace 1060 | 1061 | QHP_NAMESPACE = org.doxygen.Project 1062 | 1063 | # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 1064 | # Qt Help Project output. For more information please see 1065 | # http://doc.trolltech.com/qthelpproject.html#virtual-folders 1066 | 1067 | QHP_VIRTUAL_FOLDER = doc 1068 | 1069 | # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to 1070 | # add. For more information please see 1071 | # http://doc.trolltech.com/qthelpproject.html#custom-filters 1072 | 1073 | QHP_CUST_FILTER_NAME = 1074 | 1075 | # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the 1076 | # custom filter to add. For more information please see 1077 | # 1078 | # Qt Help Project / Custom Filters. 1079 | 1080 | QHP_CUST_FILTER_ATTRS = 1081 | 1082 | # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this 1083 | # project's 1084 | # filter section matches. 1085 | # 1086 | # Qt Help Project / Filter Attributes. 1087 | 1088 | QHP_SECT_FILTER_ATTRS = 1089 | 1090 | # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 1091 | # be used to specify the location of Qt's qhelpgenerator. 1092 | # If non-empty doxygen will try to run qhelpgenerator on the generated 1093 | # .qhp file. 1094 | 1095 | QHG_LOCATION = 1096 | 1097 | # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files 1098 | # will be generated, which together with the HTML files, form an Eclipse help 1099 | # plugin. To install this plugin and make it available under the help contents 1100 | # menu in Eclipse, the contents of the directory containing the HTML and XML 1101 | # files needs to be copied into the plugins directory of eclipse. The name of 1102 | # the directory within the plugins directory should be the same as 1103 | # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before 1104 | # the help appears. 1105 | 1106 | GENERATE_ECLIPSEHELP = NO 1107 | 1108 | # A unique identifier for the eclipse help plugin. When installing the plugin 1109 | # the directory name containing the HTML and XML files should also have 1110 | # this name. 1111 | 1112 | ECLIPSE_DOC_ID = org.doxygen.Project 1113 | 1114 | # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) 1115 | # at top of each HTML page. The value NO (the default) enables the index and 1116 | # the value YES disables it. Since the tabs have the same information as the 1117 | # navigation tree you can set this option to NO if you already set 1118 | # GENERATE_TREEVIEW to YES. 1119 | 1120 | DISABLE_INDEX = NO 1121 | 1122 | # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 1123 | # structure should be generated to display hierarchical information. 1124 | # If the tag value is set to YES, a side panel will be generated 1125 | # containing a tree-like index structure (just like the one that 1126 | # is generated for HTML Help). For this to work a browser that supports 1127 | # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). 1128 | # Windows users are probably better off using the HTML help feature. 1129 | # Since the tree basically has the same information as the tab index you 1130 | # could consider to set DISABLE_INDEX to NO when enabling this option. 1131 | 1132 | GENERATE_TREEVIEW = NO 1133 | 1134 | # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 1135 | # (range [0,1..20]) that doxygen will group on one line in the generated HTML 1136 | # documentation. Note that a value of 0 will completely suppress the enum 1137 | # values from appearing in the overview section. 1138 | 1139 | ENUM_VALUES_PER_LINE = 4 1140 | 1141 | # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 1142 | # used to set the initial width (in pixels) of the frame in which the tree 1143 | # is shown. 1144 | 1145 | TREEVIEW_WIDTH = 250 1146 | 1147 | # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open 1148 | # links to external symbols imported via tag files in a separate window. 1149 | 1150 | EXT_LINKS_IN_WINDOW = NO 1151 | 1152 | # Use this tag to change the font size of Latex formulas included 1153 | # as images in the HTML documentation. The default is 10. Note that 1154 | # when you change the font size after a successful doxygen run you need 1155 | # to manually remove any form_*.png images from the HTML output directory 1156 | # to force them to be regenerated. 1157 | 1158 | FORMULA_FONTSIZE = 10 1159 | 1160 | # Use the FORMULA_TRANPARENT tag to determine whether or not the images 1161 | # generated for formulas are transparent PNGs. Transparent PNGs are 1162 | # not supported properly for IE 6.0, but are supported on all modern browsers. 1163 | # Note that when changing this option you need to delete any form_*.png files 1164 | # in the HTML output before the changes have effect. 1165 | 1166 | FORMULA_TRANSPARENT = YES 1167 | 1168 | # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax 1169 | # (see http://www.mathjax.org) which uses client side Javascript for the 1170 | # rendering instead of using prerendered bitmaps. Use this if you do not 1171 | # have LaTeX installed or if you want to formulas look prettier in the HTML 1172 | # output. When enabled you may also need to install MathJax separately and 1173 | # configure the path to it using the MATHJAX_RELPATH option. 1174 | 1175 | USE_MATHJAX = YES 1176 | 1177 | # When MathJax is enabled you need to specify the location relative to the 1178 | # HTML output directory using the MATHJAX_RELPATH option. The destination 1179 | # directory should contain the MathJax.js script. For instance, if the mathjax 1180 | # directory is located at the same level as the HTML output directory, then 1181 | # MATHJAX_RELPATH should be ../mathjax. The default value points to 1182 | # the MathJax Content Delivery Network so you can quickly see the result without 1183 | # installing MathJax. 1184 | # However, it is strongly recommended to install a local 1185 | # copy of MathJax from http://www.mathjax.org before deployment. 1186 | 1187 | MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest 1188 | 1189 | # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension 1190 | # names that should be enabled during MathJax rendering. 1191 | 1192 | MATHJAX_EXTENSIONS = 1193 | 1194 | # When the SEARCHENGINE tag is enabled doxygen will generate a search box 1195 | # for the HTML output. The underlying search engine uses javascript 1196 | # and DHTML and should work on any modern browser. Note that when using 1197 | # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets 1198 | # (GENERATE_DOCSET) there is already a search function so this one should 1199 | # typically be disabled. For large projects the javascript based search engine 1200 | # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. 1201 | 1202 | SEARCHENGINE = YES 1203 | 1204 | # When the SERVER_BASED_SEARCH tag is enabled the search engine will be 1205 | # implemented using a PHP enabled web server instead of at the web client 1206 | # using Javascript. Doxygen will generate the search PHP script and index 1207 | # file to put on the web server. The advantage of the server 1208 | # based approach is that it scales better to large projects and allows 1209 | # full text search. The disadvantages are that it is more difficult to setup 1210 | # and does not have live searching capabilities. 1211 | 1212 | SERVER_BASED_SEARCH = NO 1213 | 1214 | #--------------------------------------------------------------------------- 1215 | # configuration options related to the LaTeX output 1216 | #--------------------------------------------------------------------------- 1217 | 1218 | # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 1219 | # generate Latex output. 1220 | 1221 | GENERATE_LATEX = YES 1222 | 1223 | # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 1224 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1225 | # put in front of it. If left blank `latex' will be used as the default path. 1226 | 1227 | LATEX_OUTPUT = latex 1228 | 1229 | # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 1230 | # invoked. If left blank `latex' will be used as the default command name. 1231 | # Note that when enabling USE_PDFLATEX this option is only used for 1232 | # generating bitmaps for formulas in the HTML output, but not in the 1233 | # Makefile that is written to the output directory. 1234 | 1235 | LATEX_CMD_NAME = latex 1236 | 1237 | # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 1238 | # generate index for LaTeX. If left blank `makeindex' will be used as the 1239 | # default command name. 1240 | 1241 | MAKEINDEX_CMD_NAME = makeindex 1242 | 1243 | # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 1244 | # LaTeX documents. This may be useful for small projects and may help to 1245 | # save some trees in general. 1246 | 1247 | COMPACT_LATEX = NO 1248 | 1249 | # The PAPER_TYPE tag can be used to set the paper type that is used 1250 | # by the printer. Possible values are: a4, letter, legal and 1251 | # executive. If left blank a4wide will be used. 1252 | 1253 | PAPER_TYPE = a4 1254 | 1255 | # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 1256 | # packages that should be included in the LaTeX output. 1257 | 1258 | EXTRA_PACKAGES = 1259 | 1260 | # The LATEX_HEADER tag can be used to specify a personal LaTeX header for 1261 | # the generated latex document. The header should contain everything until 1262 | # the first chapter. If it is left blank doxygen will generate a 1263 | # standard header. Notice: only use this tag if you know what you are doing! 1264 | 1265 | LATEX_HEADER = 1266 | 1267 | # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for 1268 | # the generated latex document. The footer should contain everything after 1269 | # the last chapter. If it is left blank doxygen will generate a 1270 | # standard footer. Notice: only use this tag if you know what you are doing! 1271 | 1272 | LATEX_FOOTER = 1273 | 1274 | # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 1275 | # is prepared for conversion to pdf (using ps2pdf). The pdf file will 1276 | # contain links (just like the HTML output) instead of page references 1277 | # This makes the output suitable for online browsing using a pdf viewer. 1278 | 1279 | PDF_HYPERLINKS = YES 1280 | 1281 | # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 1282 | # plain latex in the generated Makefile. Set this option to YES to get a 1283 | # higher quality PDF documentation. 1284 | 1285 | USE_PDFLATEX = YES 1286 | 1287 | # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 1288 | # command to the generated LaTeX files. This will instruct LaTeX to keep 1289 | # running if errors occur, instead of asking the user for help. 1290 | # This option is also used when generating formulas in HTML. 1291 | 1292 | LATEX_BATCHMODE = NO 1293 | 1294 | # If LATEX_HIDE_INDICES is set to YES then doxygen will not 1295 | # include the index chapters (such as File Index, Compound Index, etc.) 1296 | # in the output. 1297 | 1298 | LATEX_HIDE_INDICES = NO 1299 | 1300 | # If LATEX_SOURCE_CODE is set to YES then doxygen will include 1301 | # source code with syntax highlighting in the LaTeX output. 1302 | # Note that which sources are shown also depends on other settings 1303 | # such as SOURCE_BROWSER. 1304 | 1305 | LATEX_SOURCE_CODE = NO 1306 | 1307 | # The LATEX_BIB_STYLE tag can be used to specify the style to use for the 1308 | # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See 1309 | # http://en.wikipedia.org/wiki/BibTeX for more info. 1310 | 1311 | LATEX_BIB_STYLE = plain 1312 | 1313 | #--------------------------------------------------------------------------- 1314 | # configuration options related to the RTF output 1315 | #--------------------------------------------------------------------------- 1316 | 1317 | # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 1318 | # The RTF output is optimized for Word 97 and may not look very pretty with 1319 | # other RTF readers or editors. 1320 | 1321 | GENERATE_RTF = NO 1322 | 1323 | # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 1324 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1325 | # put in front of it. If left blank `rtf' will be used as the default path. 1326 | 1327 | RTF_OUTPUT = rtf 1328 | 1329 | # If the COMPACT_RTF tag is set to YES Doxygen generates more compact 1330 | # RTF documents. This may be useful for small projects and may help to 1331 | # save some trees in general. 1332 | 1333 | COMPACT_RTF = NO 1334 | 1335 | # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 1336 | # will contain hyperlink fields. The RTF file will 1337 | # contain links (just like the HTML output) instead of page references. 1338 | # This makes the output suitable for online browsing using WORD or other 1339 | # programs which support those fields. 1340 | # Note: wordpad (write) and others do not support links. 1341 | 1342 | RTF_HYPERLINKS = NO 1343 | 1344 | # Load style sheet definitions from file. Syntax is similar to doxygen's 1345 | # config file, i.e. a series of assignments. You only have to provide 1346 | # replacements, missing definitions are set to their default value. 1347 | 1348 | RTF_STYLESHEET_FILE = 1349 | 1350 | # Set optional variables used in the generation of an rtf document. 1351 | # Syntax is similar to doxygen's config file. 1352 | 1353 | RTF_EXTENSIONS_FILE = 1354 | 1355 | #--------------------------------------------------------------------------- 1356 | # configuration options related to the man page output 1357 | #--------------------------------------------------------------------------- 1358 | 1359 | # If the GENERATE_MAN tag is set to YES (the default) Doxygen will 1360 | # generate man pages 1361 | 1362 | GENERATE_MAN = NO 1363 | 1364 | # The MAN_OUTPUT tag is used to specify where the man pages will be put. 1365 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1366 | # put in front of it. If left blank `man' will be used as the default path. 1367 | 1368 | MAN_OUTPUT = man 1369 | 1370 | # The MAN_EXTENSION tag determines the extension that is added to 1371 | # the generated man pages (default is the subroutine's section .3) 1372 | 1373 | MAN_EXTENSION = .3 1374 | 1375 | # If the MAN_LINKS tag is set to YES and Doxygen generates man output, 1376 | # then it will generate one additional man file for each entity 1377 | # documented in the real man page(s). These additional files 1378 | # only source the real man page, but without them the man command 1379 | # would be unable to find the correct page. The default is NO. 1380 | 1381 | MAN_LINKS = NO 1382 | 1383 | #--------------------------------------------------------------------------- 1384 | # configuration options related to the XML output 1385 | #--------------------------------------------------------------------------- 1386 | 1387 | # If the GENERATE_XML tag is set to YES Doxygen will 1388 | # generate an XML file that captures the structure of 1389 | # the code including all documentation. 1390 | 1391 | GENERATE_XML = NO 1392 | 1393 | # The XML_OUTPUT tag is used to specify where the XML pages will be put. 1394 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 1395 | # put in front of it. If left blank `xml' will be used as the default path. 1396 | 1397 | XML_OUTPUT = xml 1398 | 1399 | # The XML_SCHEMA tag can be used to specify an XML schema, 1400 | # which can be used by a validating XML parser to check the 1401 | # syntax of the XML files. 1402 | 1403 | XML_SCHEMA = 1404 | 1405 | # The XML_DTD tag can be used to specify an XML DTD, 1406 | # which can be used by a validating XML parser to check the 1407 | # syntax of the XML files. 1408 | 1409 | XML_DTD = 1410 | 1411 | # If the XML_PROGRAMLISTING tag is set to YES Doxygen will 1412 | # dump the program listings (including syntax highlighting 1413 | # and cross-referencing information) to the XML output. Note that 1414 | # enabling this will significantly increase the size of the XML output. 1415 | 1416 | XML_PROGRAMLISTING = YES 1417 | 1418 | #--------------------------------------------------------------------------- 1419 | # configuration options for the AutoGen Definitions output 1420 | #--------------------------------------------------------------------------- 1421 | 1422 | # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 1423 | # generate an AutoGen Definitions (see autogen.sf.net) file 1424 | # that captures the structure of the code including all 1425 | # documentation. Note that this feature is still experimental 1426 | # and incomplete at the moment. 1427 | 1428 | GENERATE_AUTOGEN_DEF = NO 1429 | 1430 | #--------------------------------------------------------------------------- 1431 | # configuration options related to the Perl module output 1432 | #--------------------------------------------------------------------------- 1433 | 1434 | # If the GENERATE_PERLMOD tag is set to YES Doxygen will 1435 | # generate a Perl module file that captures the structure of 1436 | # the code including all documentation. Note that this 1437 | # feature is still experimental and incomplete at the 1438 | # moment. 1439 | 1440 | GENERATE_PERLMOD = NO 1441 | 1442 | # If the PERLMOD_LATEX tag is set to YES Doxygen will generate 1443 | # the necessary Makefile rules, Perl scripts and LaTeX code to be able 1444 | # to generate PDF and DVI output from the Perl module output. 1445 | 1446 | PERLMOD_LATEX = NO 1447 | 1448 | # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 1449 | # nicely formatted so it can be parsed by a human reader. 1450 | # This is useful 1451 | # if you want to understand what is going on. 1452 | # On the other hand, if this 1453 | # tag is set to NO the size of the Perl module output will be much smaller 1454 | # and Perl will parse it just the same. 1455 | 1456 | PERLMOD_PRETTY = YES 1457 | 1458 | # The names of the make variables in the generated doxyrules.make file 1459 | # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 1460 | # This is useful so different doxyrules.make files included by the same 1461 | # Makefile don't overwrite each other's variables. 1462 | 1463 | PERLMOD_MAKEVAR_PREFIX = 1464 | 1465 | #--------------------------------------------------------------------------- 1466 | # Configuration options related to the preprocessor 1467 | #--------------------------------------------------------------------------- 1468 | 1469 | # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 1470 | # evaluate all C-preprocessor directives found in the sources and include 1471 | # files. 1472 | 1473 | ENABLE_PREPROCESSING = YES 1474 | 1475 | # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 1476 | # names in the source code. If set to NO (the default) only conditional 1477 | # compilation will be performed. Macro expansion can be done in a controlled 1478 | # way by setting EXPAND_ONLY_PREDEF to YES. 1479 | 1480 | MACRO_EXPANSION = NO 1481 | 1482 | # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 1483 | # then the macro expansion is limited to the macros specified with the 1484 | # PREDEFINED and EXPAND_AS_DEFINED tags. 1485 | 1486 | EXPAND_ONLY_PREDEF = NO 1487 | 1488 | # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 1489 | # pointed to by INCLUDE_PATH will be searched when a #include is found. 1490 | 1491 | SEARCH_INCLUDES = YES 1492 | 1493 | # The INCLUDE_PATH tag can be used to specify one or more directories that 1494 | # contain include files that are not input files but should be processed by 1495 | # the preprocessor. 1496 | 1497 | INCLUDE_PATH = 1498 | 1499 | # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 1500 | # patterns (like *.h and *.hpp) to filter out the header-files in the 1501 | # directories. If left blank, the patterns specified with FILE_PATTERNS will 1502 | # be used. 1503 | 1504 | INCLUDE_FILE_PATTERNS = 1505 | 1506 | # The PREDEFINED tag can be used to specify one or more macro names that 1507 | # are defined before the preprocessor is started (similar to the -D option of 1508 | # gcc). The argument of the tag is a list of macros of the form: name 1509 | # or name=definition (no spaces). If the definition and the = are 1510 | # omitted =1 is assumed. To prevent a macro definition from being 1511 | # undefined via #undef or recursively expanded use the := operator 1512 | # instead of the = operator. 1513 | 1514 | PREDEFINED = 1515 | 1516 | # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 1517 | # this tag can be used to specify a list of macro names that should be expanded. 1518 | # The macro definition that is found in the sources will be used. 1519 | # Use the PREDEFINED tag if you want to use a different macro definition that 1520 | # overrules the definition found in the source code. 1521 | 1522 | EXPAND_AS_DEFINED = 1523 | 1524 | # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 1525 | # doxygen's preprocessor will remove all references to function-like macros 1526 | # that are alone on a line, have an all uppercase name, and do not end with a 1527 | # semicolon, because these will confuse the parser if not removed. 1528 | 1529 | SKIP_FUNCTION_MACROS = YES 1530 | 1531 | #--------------------------------------------------------------------------- 1532 | # Configuration::additions related to external references 1533 | #--------------------------------------------------------------------------- 1534 | 1535 | # The TAGFILES option can be used to specify one or more tagfiles. For each 1536 | # tag file the location of the external documentation should be added. The 1537 | # format of a tag file without this location is as follows: 1538 | # 1539 | # TAGFILES = file1 file2 ... 1540 | # Adding location for the tag files is done as follows: 1541 | # 1542 | # TAGFILES = file1=loc1 "file2 = loc2" ... 1543 | # where "loc1" and "loc2" can be relative or absolute paths 1544 | # or URLs. Note that each tag file must have a unique name (where the name does 1545 | # NOT include the path). If a tag file is not located in the directory in which 1546 | # doxygen is run, you must also specify the path to the tagfile here. 1547 | 1548 | TAGFILES = 1549 | 1550 | # When a file name is specified after GENERATE_TAGFILE, doxygen will create 1551 | # a tag file that is based on the input files it reads. 1552 | 1553 | GENERATE_TAGFILE = 1554 | 1555 | # If the ALLEXTERNALS tag is set to YES all external classes will be listed 1556 | # in the class index. If set to NO only the inherited external classes 1557 | # will be listed. 1558 | 1559 | ALLEXTERNALS = NO 1560 | 1561 | # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 1562 | # in the modules index. If set to NO, only the current project's groups will 1563 | # be listed. 1564 | 1565 | EXTERNAL_GROUPS = YES 1566 | 1567 | # The PERL_PATH should be the absolute path and name of the perl script 1568 | # interpreter (i.e. the result of `which perl'). 1569 | 1570 | PERL_PATH = /usr/bin/perl 1571 | 1572 | #--------------------------------------------------------------------------- 1573 | # Configuration options related to the dot tool 1574 | #--------------------------------------------------------------------------- 1575 | 1576 | # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 1577 | # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 1578 | # or super classes. Setting the tag to NO turns the diagrams off. Note that 1579 | # this option also works with HAVE_DOT disabled, but it is recommended to 1580 | # install and use dot, since it yields more powerful graphs. 1581 | 1582 | CLASS_DIAGRAMS = YES 1583 | 1584 | # You can define message sequence charts within doxygen comments using the \msc 1585 | # command. Doxygen will then run the mscgen tool (see 1586 | # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 1587 | # documentation. The MSCGEN_PATH tag allows you to specify the directory where 1588 | # the mscgen tool resides. If left empty the tool is assumed to be found in the 1589 | # default search path. 1590 | 1591 | MSCGEN_PATH = 1592 | 1593 | # If set to YES, the inheritance and collaboration graphs will hide 1594 | # inheritance and usage relations if the target is undocumented 1595 | # or is not a class. 1596 | 1597 | HIDE_UNDOC_RELATIONS = YES 1598 | 1599 | # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 1600 | # available from the path. This tool is part of Graphviz, a graph visualization 1601 | # toolkit from AT&T and Lucent Bell Labs. The other options in this section 1602 | # have no effect if this option is set to NO (the default) 1603 | 1604 | HAVE_DOT = NO 1605 | 1606 | # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is 1607 | # allowed to run in parallel. When set to 0 (the default) doxygen will 1608 | # base this on the number of processors available in the system. You can set it 1609 | # explicitly to a value larger than 0 to get control over the balance 1610 | # between CPU load and processing speed. 1611 | 1612 | DOT_NUM_THREADS = 0 1613 | 1614 | # By default doxygen will use the Helvetica font for all dot files that 1615 | # doxygen generates. When you want a differently looking font you can specify 1616 | # the font name using DOT_FONTNAME. You need to make sure dot is able to find 1617 | # the font, which can be done by putting it in a standard location or by setting 1618 | # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the 1619 | # directory containing the font. 1620 | 1621 | DOT_FONTNAME = Helvetica 1622 | 1623 | # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 1624 | # The default size is 10pt. 1625 | 1626 | DOT_FONTSIZE = 10 1627 | 1628 | # By default doxygen will tell dot to use the Helvetica font. 1629 | # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to 1630 | # set the path where dot can find it. 1631 | 1632 | DOT_FONTPATH = 1633 | 1634 | # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 1635 | # will generate a graph for each documented class showing the direct and 1636 | # indirect inheritance relations. Setting this tag to YES will force the 1637 | # CLASS_DIAGRAMS tag to NO. 1638 | 1639 | CLASS_GRAPH = YES 1640 | 1641 | # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 1642 | # will generate a graph for each documented class showing the direct and 1643 | # indirect implementation dependencies (inheritance, containment, and 1644 | # class references variables) of the class with other documented classes. 1645 | 1646 | COLLABORATION_GRAPH = YES 1647 | 1648 | # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 1649 | # will generate a graph for groups, showing the direct groups dependencies 1650 | 1651 | GROUP_GRAPHS = YES 1652 | 1653 | # If the UML_LOOK tag is set to YES doxygen will generate inheritance and 1654 | # collaboration diagrams in a style similar to the OMG's Unified Modeling 1655 | # Language. 1656 | 1657 | UML_LOOK = NO 1658 | 1659 | # If the UML_LOOK tag is enabled, the fields and methods are shown inside 1660 | # the class node. If there are many fields or methods and many nodes the 1661 | # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS 1662 | # threshold limits the number of items for each type to make the size more 1663 | # managable. Set this to 0 for no limit. Note that the threshold may be 1664 | # exceeded by 50% before the limit is enforced. 1665 | 1666 | UML_LIMIT_NUM_FIELDS = 10 1667 | 1668 | # If set to YES, the inheritance and collaboration graphs will show the 1669 | # relations between templates and their instances. 1670 | 1671 | TEMPLATE_RELATIONS = NO 1672 | 1673 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 1674 | # tags are set to YES then doxygen will generate a graph for each documented 1675 | # file showing the direct and indirect include dependencies of the file with 1676 | # other documented files. 1677 | 1678 | INCLUDE_GRAPH = YES 1679 | 1680 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 1681 | # HAVE_DOT tags are set to YES then doxygen will generate a graph for each 1682 | # documented header file showing the documented files that directly or 1683 | # indirectly include this file. 1684 | 1685 | INCLUDED_BY_GRAPH = YES 1686 | 1687 | # If the CALL_GRAPH and HAVE_DOT options are set to YES then 1688 | # doxygen will generate a call dependency graph for every global function 1689 | # or class method. Note that enabling this option will significantly increase 1690 | # the time of a run. So in most cases it will be better to enable call graphs 1691 | # for selected functions only using the \callgraph command. 1692 | 1693 | CALL_GRAPH = NO 1694 | 1695 | # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 1696 | # doxygen will generate a caller dependency graph for every global function 1697 | # or class method. Note that enabling this option will significantly increase 1698 | # the time of a run. So in most cases it will be better to enable caller 1699 | # graphs for selected functions only using the \callergraph command. 1700 | 1701 | CALLER_GRAPH = NO 1702 | 1703 | # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 1704 | # will generate a graphical hierarchy of all classes instead of a textual one. 1705 | 1706 | GRAPHICAL_HIERARCHY = YES 1707 | 1708 | # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES 1709 | # then doxygen will show the dependencies a directory has on other directories 1710 | # in a graphical way. The dependency relations are determined by the #include 1711 | # relations between the files in the directories. 1712 | 1713 | DIRECTORY_GRAPH = YES 1714 | 1715 | # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 1716 | # generated by dot. Possible values are svg, png, jpg, or gif. 1717 | # If left blank png will be used. If you choose svg you need to set 1718 | # HTML_FILE_EXTENSION to xhtml in order to make the SVG files 1719 | # visible in IE 9+ (other browsers do not have this requirement). 1720 | 1721 | DOT_IMAGE_FORMAT = png 1722 | 1723 | # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to 1724 | # enable generation of interactive SVG images that allow zooming and panning. 1725 | # Note that this requires a modern browser other than Internet Explorer. 1726 | # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you 1727 | # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files 1728 | # visible. Older versions of IE do not have SVG support. 1729 | 1730 | INTERACTIVE_SVG = NO 1731 | 1732 | # The tag DOT_PATH can be used to specify the path where the dot tool can be 1733 | # found. If left blank, it is assumed the dot tool can be found in the path. 1734 | 1735 | DOT_PATH = 1736 | 1737 | # The DOTFILE_DIRS tag can be used to specify one or more directories that 1738 | # contain dot files that are included in the documentation (see the 1739 | # \dotfile command). 1740 | 1741 | DOTFILE_DIRS = 1742 | 1743 | # The MSCFILE_DIRS tag can be used to specify one or more directories that 1744 | # contain msc files that are included in the documentation (see the 1745 | # \mscfile command). 1746 | 1747 | MSCFILE_DIRS = 1748 | 1749 | # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 1750 | # nodes that will be shown in the graph. If the number of nodes in a graph 1751 | # becomes larger than this value, doxygen will truncate the graph, which is 1752 | # visualized by representing a node as a red box. Note that doxygen if the 1753 | # number of direct children of the root node in a graph is already larger than 1754 | # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 1755 | # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. 1756 | 1757 | DOT_GRAPH_MAX_NODES = 50 1758 | 1759 | # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 1760 | # graphs generated by dot. A depth value of 3 means that only nodes reachable 1761 | # from the root by following a path via at most 3 edges will be shown. Nodes 1762 | # that lay further from the root node will be omitted. Note that setting this 1763 | # option to 1 or 2 may greatly reduce the computation time needed for large 1764 | # code bases. Also note that the size of a graph can be further restricted by 1765 | # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. 1766 | 1767 | MAX_DOT_GRAPH_DEPTH = 0 1768 | 1769 | # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 1770 | # background. This is disabled by default, because dot on Windows does not 1771 | # seem to support this out of the box. Warning: Depending on the platform used, 1772 | # enabling this option may lead to badly anti-aliased labels on the edges of 1773 | # a graph (i.e. they become hard to read). 1774 | 1775 | DOT_TRANSPARENT = NO 1776 | 1777 | # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 1778 | # files in one run (i.e. multiple -o and -T options on the command line). This 1779 | # makes dot run faster, but since only newer versions of dot (>1.8.10) 1780 | # support this, this feature is disabled by default. 1781 | 1782 | DOT_MULTI_TARGETS = NO 1783 | 1784 | # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 1785 | # generate a legend page explaining the meaning of the various boxes and 1786 | # arrows in the dot generated graphs. 1787 | 1788 | GENERATE_LEGEND = YES 1789 | 1790 | # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 1791 | # remove the intermediate dot files that are used to generate 1792 | # the various graphs. 1793 | 1794 | DOT_CLEANUP = YES 1795 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(parse parse.cpp) 2 | target_link_libraries(parse cpptoml) 3 | set_target_properties(parse PROPERTIES 4 | CXX_STANDARD 11 5 | CXX_EXTENSIONS OFF 6 | CXX_STANDARD_REQUIRED YES) 7 | 8 | add_executable(cpptoml-parser parse_stdin.cpp) 9 | target_link_libraries(cpptoml-parser cpptoml) 10 | set_target_properties(cpptoml-parser PROPERTIES 11 | CXX_STANDARD 11 12 | CXX_EXTENSIONS OFF 13 | CXX_STANDARD_REQUIRED YES) 14 | 15 | add_executable(cpptoml-build build_toml.cpp) 16 | target_link_libraries(cpptoml-build cpptoml) 17 | set_target_properties(cpptoml-build PROPERTIES 18 | CXX_STANDARD 11 19 | CXX_EXTENSIONS OFF 20 | CXX_STANDARD_REQUIRED YES) 21 | 22 | add_executable(cpptoml-conversions conversions.cpp) 23 | target_link_libraries(cpptoml-conversions cpptoml) 24 | set_target_properties(cpptoml-conversions PROPERTIES 25 | CXX_STANDARD 11 26 | CXX_EXTENSIONS OFF 27 | CXX_STANDARD_REQUIRED YES) 28 | -------------------------------------------------------------------------------- /examples/build_toml.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char* argv[]) 5 | { 6 | std::shared_ptr root = cpptoml::make_table(); 7 | root->insert("Integer", 1234L); 8 | root->insert("Double", 1.234); 9 | root->insert("String", std::string("ABCD")); 10 | 11 | auto table = cpptoml::make_table(); 12 | table->insert("ElementOne", 1L); 13 | table->insert("ElementTwo", 2.0); 14 | table->insert("ElementThree", std::string("THREE")); 15 | 16 | auto nested_table = cpptoml::make_table(); 17 | nested_table->insert("ElementOne", 2L); 18 | nested_table->insert("ElementTwo", 3.0); 19 | nested_table->insert("ElementThree", std::string("FOUR")); 20 | 21 | table->insert("Nested", nested_table); 22 | 23 | root->insert("Table", table); 24 | 25 | auto int_array = cpptoml::make_array(); 26 | int_array->push_back(1L); 27 | int_array->push_back(2L); 28 | int_array->push_back(3L); 29 | int_array->push_back(4L); 30 | int_array->push_back(5L); 31 | 32 | root->insert("IntegerArray", int_array); 33 | 34 | auto double_array = cpptoml::make_array(); 35 | double_array->push_back(1.1); 36 | double_array->push_back(2.2); 37 | double_array->push_back(3.3); 38 | double_array->push_back(4.4); 39 | double_array->push_back(5.5); 40 | 41 | root->insert("DoubleArray", double_array); 42 | 43 | auto string_array = cpptoml::make_array(); 44 | string_array->push_back(std::string("A")); 45 | string_array->push_back(std::string("B")); 46 | string_array->push_back(std::string("C")); 47 | string_array->push_back(std::string("D")); 48 | string_array->push_back(std::string("E")); 49 | 50 | root->insert("StringArray", string_array); 51 | 52 | auto table_array = cpptoml::make_table_array(); 53 | table_array->push_back(table); 54 | table_array->push_back(table); 55 | table_array->push_back(table); 56 | 57 | root->insert("TableArray", table_array); 58 | 59 | auto array_of_arrays = cpptoml::make_array(); 60 | array_of_arrays->push_back(int_array); 61 | array_of_arrays->push_back(double_array); 62 | array_of_arrays->push_back(string_array); 63 | 64 | root->insert("ArrayOfArrays", array_of_arrays); 65 | 66 | std::cout << (*root); 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /examples/conversions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "cpptoml.h" 5 | 6 | int main() 7 | { 8 | auto root = cpptoml::make_table(); 9 | 10 | root->insert("small-integer", int64_t{12}); 11 | 12 | auto si = *root->get_as("small-integer"); 13 | 14 | root->insert("small-integer2", si); 15 | 16 | try 17 | { 18 | root->insert("too-big", std::numeric_limits::max()); 19 | } 20 | catch (std::overflow_error&) 21 | { 22 | } 23 | 24 | root->insert("medium-integer", std::numeric_limits::max()); 25 | 26 | try 27 | { 28 | root->get_as("medium-integer"); 29 | } 30 | catch (std::overflow_error&) 31 | { 32 | } 33 | 34 | root->get_as("medium-integer"); // signed as unsigned, checked 35 | 36 | root->insert("medium-negative", std::numeric_limits::min()); 37 | 38 | try 39 | { 40 | root->get_as("medium-negative"); 41 | } 42 | catch (std::underflow_error&) 43 | { 44 | } 45 | 46 | try 47 | { 48 | root->get_as("medium-negative"); 49 | } 50 | catch (std::underflow_error&) 51 | { 52 | } 53 | 54 | root->get_as("medium-negative"); 55 | 56 | root->insert("float", 0.1f); 57 | root->get_as("float"); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /examples/parse.cpp: -------------------------------------------------------------------------------- 1 | #include "cpptoml.h" 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char** argv) 7 | { 8 | if (argc < 2) 9 | { 10 | std::cout << "Usage: " << argv[0] << " filename" << std::endl; 11 | return 1; 12 | } 13 | 14 | try 15 | { 16 | std::shared_ptr g = cpptoml::parse_file(argv[1]); 17 | std::cout << (*g) << std::endl; 18 | } 19 | catch (const cpptoml::parse_exception& e) 20 | { 21 | std::cerr << "Failed to parse " << argv[1] << ": " << e.what() << std::endl; 22 | return 1; 23 | } 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /examples/parse_stdin.cpp: -------------------------------------------------------------------------------- 1 | #include "cpptoml.h" 2 | 3 | #include 4 | #include 5 | 6 | /** 7 | * A visitor for toml objects that writes to an output stream in the JSON 8 | * format that the toml-test suite expects. 9 | */ 10 | class toml_test_writer 11 | { 12 | public: 13 | toml_test_writer(std::ostream& s) : stream_(s) 14 | { 15 | // nothing 16 | } 17 | 18 | void visit(const cpptoml::value& v) 19 | { 20 | stream_ << "{\"type\":\"string\",\"value\":\"" 21 | << cpptoml::toml_writer::escape_string(v.get()) << "\"}"; 22 | } 23 | 24 | void visit(const cpptoml::value& v) 25 | { 26 | stream_ << "{\"type\":\"integer\",\"value\":\"" << v.get() << "\"}"; 27 | } 28 | 29 | void visit(const cpptoml::value& v) 30 | { 31 | stream_ << "{\"type\":\"float\",\"value\":\"" << v.get() << "\"}"; 32 | } 33 | 34 | void visit(const cpptoml::value& v) 35 | { 36 | stream_ << "{\"type\":\"local_date\",\"value\":\"" << v.get() << "\"}"; 37 | } 38 | 39 | void visit(const cpptoml::value& v) 40 | { 41 | stream_ << "{\"type\":\"local_time\",\"value\":\"" << v.get() << "\"}"; 42 | } 43 | 44 | void visit(const cpptoml::value& v) 45 | { 46 | stream_ << "{\"type\":\"local_datetime\",\"value\":\"" << v.get() 47 | << "\"}"; 48 | } 49 | 50 | void visit(const cpptoml::value& v) 51 | { 52 | stream_ << "{\"type\":\"datetime\",\"value\":\"" << v.get() << "\"}"; 53 | } 54 | 55 | void visit(const cpptoml::value& v) 56 | { 57 | stream_ << "{\"type\":\"bool\",\"value\":\"" << v << "\"}"; 58 | } 59 | 60 | void visit(const cpptoml::array& arr) 61 | { 62 | stream_ << "{\"type\":\"array\",\"value\":["; 63 | auto it = arr.get().begin(); 64 | while (it != arr.get().end()) 65 | { 66 | (*it)->accept(*this); 67 | if (++it != arr.get().end()) 68 | stream_ << ", "; 69 | } 70 | stream_ << "]}"; 71 | } 72 | 73 | void visit(const cpptoml::table_array& tarr) 74 | { 75 | stream_ << "["; 76 | auto arr = tarr.get(); 77 | auto ait = arr.begin(); 78 | while (ait != arr.end()) 79 | { 80 | (*ait)->accept(*this); 81 | if (++ait != arr.end()) 82 | stream_ << ", "; 83 | } 84 | stream_ << "]"; 85 | } 86 | 87 | void visit(const cpptoml::table& t) 88 | { 89 | stream_ << "{"; 90 | auto it = t.begin(); 91 | while (it != t.end()) 92 | { 93 | stream_ << '"' << cpptoml::toml_writer::escape_string(it->first) 94 | << "\":"; 95 | it->second->accept(*this); 96 | if (++it != t.end()) 97 | stream_ << ", "; 98 | } 99 | stream_ << "}"; 100 | } 101 | 102 | private: 103 | std::ostream& stream_; 104 | }; 105 | 106 | int main() 107 | { 108 | std::cout.precision(std::numeric_limits::max_digits10); 109 | cpptoml::parser p{std::cin}; 110 | try 111 | { 112 | std::shared_ptr g = p.parse(); 113 | toml_test_writer writer{std::cout}; 114 | g->accept(writer); 115 | std::cout << std::endl; 116 | } 117 | catch (const cpptoml::parse_exception& ex) 118 | { 119 | std::cerr << "Parsing failed: " << ex.what() << std::endl; 120 | return 1; 121 | } 122 | catch (...) 123 | { 124 | std::cerr << "Something horrible happened!" << std::endl; 125 | // return as if there was success so that toml-test will complain 126 | return 0; 127 | } 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /include/cpptoml.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file cpptoml.h 3 | * @author Chase Geigle 4 | * @date May 2013 5 | */ 6 | 7 | #ifndef CPPTOML_H 8 | #define CPPTOML_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #if __cplusplus > 201103L 26 | #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]] 27 | #elif defined(__clang__) 28 | #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason))) 29 | #elif defined(__GNUG__) 30 | #define CPPTOML_DEPRECATED(reason) __attribute__((deprecated)) 31 | #elif defined(_MSC_VER) 32 | #if _MSC_VER < 1910 33 | #define CPPTOML_DEPRECATED(reason) __declspec(deprecated) 34 | #else 35 | #define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]] 36 | #endif 37 | #endif 38 | 39 | namespace cpptoml 40 | { 41 | class writer; // forward declaration 42 | class base; // forward declaration 43 | #if defined(CPPTOML_USE_MAP) 44 | // a std::map will ensure that entries a sorted, albeit at a slight 45 | // performance penalty relative to the (default) unordered_map 46 | using string_to_base_map = std::map>; 47 | #else 48 | // by default an unordered_map is used for best performance as the 49 | // toml specification does not require entries to be sorted 50 | using string_to_base_map 51 | = std::unordered_map>; 52 | #endif 53 | 54 | // if defined, `base` will retain type information in form of an enum class 55 | // such that static_cast can be used instead of dynamic_cast 56 | // #define CPPTOML_NO_RTTI 57 | 58 | template 59 | class option 60 | { 61 | public: 62 | option() : empty_{true} 63 | { 64 | // nothing 65 | } 66 | 67 | option(T value) : empty_{false}, value_(std::move(value)) 68 | { 69 | // nothing 70 | } 71 | 72 | explicit operator bool() const 73 | { 74 | return !empty_; 75 | } 76 | 77 | const T& operator*() const 78 | { 79 | return value_; 80 | } 81 | 82 | const T* operator->() const 83 | { 84 | return &value_; 85 | } 86 | 87 | template 88 | T value_or(U&& alternative) const 89 | { 90 | if (!empty_) 91 | return value_; 92 | return static_cast(std::forward(alternative)); 93 | } 94 | 95 | private: 96 | bool empty_; 97 | T value_; 98 | }; 99 | 100 | struct local_date 101 | { 102 | int year = 0; 103 | int month = 0; 104 | int day = 0; 105 | }; 106 | 107 | struct local_time 108 | { 109 | int hour = 0; 110 | int minute = 0; 111 | int second = 0; 112 | int microsecond = 0; 113 | }; 114 | 115 | struct zone_offset 116 | { 117 | int hour_offset = 0; 118 | int minute_offset = 0; 119 | }; 120 | 121 | struct local_datetime : local_date, local_time 122 | { 123 | }; 124 | 125 | struct offset_datetime : local_datetime, zone_offset 126 | { 127 | static inline struct offset_datetime from_zoned(const struct tm& t) 128 | { 129 | offset_datetime dt; 130 | dt.year = t.tm_year + 1900; 131 | dt.month = t.tm_mon + 1; 132 | dt.day = t.tm_mday; 133 | dt.hour = t.tm_hour; 134 | dt.minute = t.tm_min; 135 | dt.second = t.tm_sec; 136 | 137 | char buf[16]; 138 | strftime(buf, 16, "%z", &t); 139 | 140 | int offset = std::stoi(buf); 141 | dt.hour_offset = offset / 100; 142 | dt.minute_offset = offset % 100; 143 | return dt; 144 | } 145 | 146 | CPPTOML_DEPRECATED("from_local has been renamed to from_zoned") 147 | static inline struct offset_datetime from_local(const struct tm& t) 148 | { 149 | return from_zoned(t); 150 | } 151 | 152 | static inline struct offset_datetime from_utc(const struct tm& t) 153 | { 154 | offset_datetime dt; 155 | dt.year = t.tm_year + 1900; 156 | dt.month = t.tm_mon + 1; 157 | dt.day = t.tm_mday; 158 | dt.hour = t.tm_hour; 159 | dt.minute = t.tm_min; 160 | dt.second = t.tm_sec; 161 | return dt; 162 | } 163 | }; 164 | 165 | CPPTOML_DEPRECATED("datetime has been renamed to offset_datetime") 166 | typedef offset_datetime datetime; 167 | 168 | class fill_guard 169 | { 170 | public: 171 | fill_guard(std::ostream& os) : os_(os), fill_{os.fill()} 172 | { 173 | // nothing 174 | } 175 | 176 | ~fill_guard() 177 | { 178 | os_.fill(fill_); 179 | } 180 | 181 | private: 182 | std::ostream& os_; 183 | std::ostream::char_type fill_; 184 | }; 185 | 186 | inline std::ostream& operator<<(std::ostream& os, const local_date& dt) 187 | { 188 | fill_guard g{os}; 189 | os.fill('0'); 190 | 191 | using std::setw; 192 | os << setw(4) << dt.year << "-" << setw(2) << dt.month << "-" << setw(2) 193 | << dt.day; 194 | 195 | return os; 196 | } 197 | 198 | inline std::ostream& operator<<(std::ostream& os, const local_time& ltime) 199 | { 200 | fill_guard g{os}; 201 | os.fill('0'); 202 | 203 | using std::setw; 204 | os << setw(2) << ltime.hour << ":" << setw(2) << ltime.minute << ":" 205 | << setw(2) << ltime.second; 206 | 207 | if (ltime.microsecond > 0) 208 | { 209 | os << "."; 210 | int power = 100000; 211 | for (int curr_us = ltime.microsecond; curr_us; power /= 10) 212 | { 213 | auto num = curr_us / power; 214 | os << num; 215 | curr_us -= num * power; 216 | } 217 | } 218 | 219 | return os; 220 | } 221 | 222 | inline std::ostream& operator<<(std::ostream& os, const zone_offset& zo) 223 | { 224 | fill_guard g{os}; 225 | os.fill('0'); 226 | 227 | using std::setw; 228 | 229 | if (zo.hour_offset != 0 || zo.minute_offset != 0) 230 | { 231 | if (zo.hour_offset > 0) 232 | { 233 | os << "+"; 234 | } 235 | else 236 | { 237 | os << "-"; 238 | } 239 | os << setw(2) << std::abs(zo.hour_offset) << ":" << setw(2) 240 | << std::abs(zo.minute_offset); 241 | } 242 | else 243 | { 244 | os << "Z"; 245 | } 246 | 247 | return os; 248 | } 249 | 250 | inline std::ostream& operator<<(std::ostream& os, const local_datetime& dt) 251 | { 252 | return os << static_cast(dt) << "T" 253 | << static_cast(dt); 254 | } 255 | 256 | inline std::ostream& operator<<(std::ostream& os, const offset_datetime& dt) 257 | { 258 | return os << static_cast(dt) 259 | << static_cast(dt); 260 | } 261 | 262 | template 263 | struct is_one_of; 264 | 265 | template 266 | struct is_one_of : std::is_same 267 | { 268 | }; 269 | 270 | template 271 | struct is_one_of 272 | { 273 | const static bool value 274 | = std::is_same::value || is_one_of::value; 275 | }; 276 | 277 | template 278 | class value; 279 | 280 | template 281 | struct valid_value 282 | : is_one_of 284 | { 285 | }; 286 | 287 | template 288 | struct value_traits; 289 | 290 | template 291 | struct valid_value_or_string_convertible 292 | { 293 | 294 | const static bool value = valid_value::type>::value 295 | || std::is_convertible::value; 296 | }; 297 | 298 | template 299 | struct value_traits::value>::type> 301 | { 302 | using value_type = typename std::conditional< 303 | valid_value::type>::value, 304 | typename std::decay::type, std::string>::type; 305 | 306 | using type = value; 307 | 308 | static value_type construct(T&& val) 309 | { 310 | return value_type(val); 311 | } 312 | }; 313 | 314 | template 315 | struct value_traits< 316 | T, 317 | typename std::enable_if< 318 | !valid_value_or_string_convertible::value 319 | && std::is_floating_point::type>::value>::type> 320 | { 321 | using value_type = typename std::decay::type; 322 | 323 | using type = value; 324 | 325 | static value_type construct(T&& val) 326 | { 327 | return value_type(val); 328 | } 329 | }; 330 | 331 | template 332 | struct value_traits< 333 | T, typename std::enable_if< 334 | !valid_value_or_string_convertible::value 335 | && !std::is_floating_point::type>::value 336 | && std::is_signed::type>::value>::type> 337 | { 338 | using value_type = int64_t; 339 | 340 | using type = value; 341 | 342 | static value_type construct(T&& val) 343 | { 344 | if (val < (std::numeric_limits::min)()) 345 | throw std::underflow_error{"constructed value cannot be " 346 | "represented by a 64-bit signed " 347 | "integer"}; 348 | 349 | if (val > (std::numeric_limits::max)()) 350 | throw std::overflow_error{"constructed value cannot be represented " 351 | "by a 64-bit signed integer"}; 352 | 353 | return static_cast(val); 354 | } 355 | }; 356 | 357 | template 358 | struct value_traits< 359 | T, typename std::enable_if< 360 | !valid_value_or_string_convertible::value 361 | && std::is_unsigned::type>::value>::type> 362 | { 363 | using value_type = int64_t; 364 | 365 | using type = value; 366 | 367 | static value_type construct(T&& val) 368 | { 369 | if (val > static_cast((std::numeric_limits::max)())) 370 | throw std::overflow_error{"constructed value cannot be represented " 371 | "by a 64-bit signed integer"}; 372 | 373 | return static_cast(val); 374 | } 375 | }; 376 | 377 | class array; 378 | class table; 379 | class table_array; 380 | 381 | template 382 | struct array_of_trait 383 | { 384 | using return_type = option>; 385 | }; 386 | 387 | template <> 388 | struct array_of_trait 389 | { 390 | using return_type = option>>; 391 | }; 392 | 393 | template 394 | inline std::shared_ptr::type> make_value(T&& val); 395 | inline std::shared_ptr make_array(); 396 | 397 | namespace detail 398 | { 399 | template 400 | inline std::shared_ptr make_element(); 401 | } 402 | 403 | inline std::shared_ptr make_table(); 404 | inline std::shared_ptr make_table_array(bool is_inline = false); 405 | 406 | #if defined(CPPTOML_NO_RTTI) 407 | /// Base type used to store underlying data type explicitly if RTTI is disabled 408 | enum class base_type 409 | { 410 | NONE, 411 | STRING, 412 | LOCAL_TIME, 413 | LOCAL_DATE, 414 | LOCAL_DATETIME, 415 | OFFSET_DATETIME, 416 | INT, 417 | FLOAT, 418 | BOOL, 419 | TABLE, 420 | ARRAY, 421 | TABLE_ARRAY 422 | }; 423 | 424 | /// Type traits class to convert C++ types to enum member 425 | template 426 | struct base_type_traits; 427 | 428 | template <> 429 | struct base_type_traits 430 | { 431 | static const base_type type = base_type::STRING; 432 | }; 433 | 434 | template <> 435 | struct base_type_traits 436 | { 437 | static const base_type type = base_type::LOCAL_TIME; 438 | }; 439 | 440 | template <> 441 | struct base_type_traits 442 | { 443 | static const base_type type = base_type::LOCAL_DATE; 444 | }; 445 | 446 | template <> 447 | struct base_type_traits 448 | { 449 | static const base_type type = base_type::LOCAL_DATETIME; 450 | }; 451 | 452 | template <> 453 | struct base_type_traits 454 | { 455 | static const base_type type = base_type::OFFSET_DATETIME; 456 | }; 457 | 458 | template <> 459 | struct base_type_traits 460 | { 461 | static const base_type type = base_type::INT; 462 | }; 463 | 464 | template <> 465 | struct base_type_traits 466 | { 467 | static const base_type type = base_type::FLOAT; 468 | }; 469 | 470 | template <> 471 | struct base_type_traits 472 | { 473 | static const base_type type = base_type::BOOL; 474 | }; 475 | 476 | template <> 477 | struct base_type_traits
478 | { 479 | static const base_type type = base_type::TABLE; 480 | }; 481 | 482 | template <> 483 | struct base_type_traits 484 | { 485 | static const base_type type = base_type::ARRAY; 486 | }; 487 | 488 | template <> 489 | struct base_type_traits 490 | { 491 | static const base_type type = base_type::TABLE_ARRAY; 492 | }; 493 | #endif 494 | 495 | /** 496 | * A generic base TOML value used for type erasure. 497 | */ 498 | class base : public std::enable_shared_from_this 499 | { 500 | public: 501 | virtual ~base() = default; 502 | 503 | virtual std::shared_ptr clone() const = 0; 504 | 505 | /** 506 | * Determines if the given TOML element is a value. 507 | */ 508 | virtual bool is_value() const 509 | { 510 | return false; 511 | } 512 | 513 | /** 514 | * Determines if the given TOML element is a table. 515 | */ 516 | virtual bool is_table() const 517 | { 518 | return false; 519 | } 520 | 521 | /** 522 | * Converts the TOML element into a table. 523 | */ 524 | std::shared_ptr
as_table() 525 | { 526 | if (is_table()) 527 | return std::static_pointer_cast
(shared_from_this()); 528 | return nullptr; 529 | } 530 | /** 531 | * Determines if the TOML element is an array of "leaf" elements. 532 | */ 533 | virtual bool is_array() const 534 | { 535 | return false; 536 | } 537 | 538 | /** 539 | * Converts the TOML element to an array. 540 | */ 541 | std::shared_ptr as_array() 542 | { 543 | if (is_array()) 544 | return std::static_pointer_cast(shared_from_this()); 545 | return nullptr; 546 | } 547 | 548 | /** 549 | * Determines if the given TOML element is an array of tables. 550 | */ 551 | virtual bool is_table_array() const 552 | { 553 | return false; 554 | } 555 | 556 | /** 557 | * Converts the TOML element into a table array. 558 | */ 559 | std::shared_ptr as_table_array() 560 | { 561 | if (is_table_array()) 562 | return std::static_pointer_cast(shared_from_this()); 563 | return nullptr; 564 | } 565 | 566 | /** 567 | * Attempts to coerce the TOML element into a concrete TOML value 568 | * of type T. 569 | */ 570 | template 571 | std::shared_ptr> as(); 572 | 573 | template 574 | std::shared_ptr> as() const; 575 | 576 | template 577 | void accept(Visitor&& visitor, Args&&... args) const; 578 | 579 | #if defined(CPPTOML_NO_RTTI) 580 | base_type type() const 581 | { 582 | return type_; 583 | } 584 | 585 | protected: 586 | base(const base_type t) : type_(t) 587 | { 588 | // nothing 589 | } 590 | 591 | private: 592 | const base_type type_ = base_type::NONE; 593 | 594 | #else 595 | protected: 596 | base() 597 | { 598 | // nothing 599 | } 600 | #endif 601 | }; 602 | 603 | /** 604 | * A concrete TOML value representing the "leaves" of the "tree". 605 | */ 606 | template 607 | class value : public base 608 | { 609 | struct make_shared_enabler 610 | { 611 | // nothing; this is a private key accessible only to friends 612 | }; 613 | 614 | template 615 | friend std::shared_ptr::type> 616 | cpptoml::make_value(U&& val); 617 | 618 | public: 619 | static_assert(valid_value::value, "invalid value type"); 620 | 621 | std::shared_ptr clone() const override; 622 | 623 | value(const make_shared_enabler&, const T& val) : value(val) 624 | { 625 | // nothing; note that users cannot actually invoke this function 626 | // because they lack access to the make_shared_enabler. 627 | } 628 | 629 | bool is_value() const override 630 | { 631 | return true; 632 | } 633 | 634 | /** 635 | * Gets the data associated with this value. 636 | */ 637 | T& get() 638 | { 639 | return data_; 640 | } 641 | 642 | /** 643 | * Gets the data associated with this value. Const version. 644 | */ 645 | const T& get() const 646 | { 647 | return data_; 648 | } 649 | 650 | private: 651 | T data_; 652 | 653 | /** 654 | * Constructs a value from the given data. 655 | */ 656 | #if defined(CPPTOML_NO_RTTI) 657 | value(const T& val) : base(base_type_traits::type), data_(val) 658 | { 659 | } 660 | #else 661 | value(const T& val) : data_(val) 662 | { 663 | } 664 | #endif 665 | 666 | value(const value& val) = delete; 667 | value& operator=(const value& val) = delete; 668 | }; 669 | 670 | template 671 | std::shared_ptr::type> make_value(T&& val) 672 | { 673 | using value_type = typename value_traits::type; 674 | using enabler = typename value_type::make_shared_enabler; 675 | return std::make_shared( 676 | enabler{}, value_traits::construct(std::forward(val))); 677 | } 678 | 679 | template 680 | inline std::shared_ptr> base::as() 681 | { 682 | #if defined(CPPTOML_NO_RTTI) 683 | if (type() == base_type_traits::type) 684 | return std::static_pointer_cast>(shared_from_this()); 685 | else 686 | return nullptr; 687 | #else 688 | return std::dynamic_pointer_cast>(shared_from_this()); 689 | #endif 690 | } 691 | 692 | // special case value to allow getting an integer parameter as a 693 | // double value 694 | template <> 695 | inline std::shared_ptr> base::as() 696 | { 697 | #if defined(CPPTOML_NO_RTTI) 698 | if (type() == base_type::FLOAT) 699 | return std::static_pointer_cast>(shared_from_this()); 700 | 701 | if (type() == base_type::INT) 702 | { 703 | auto v = std::static_pointer_cast>(shared_from_this()); 704 | return make_value(static_cast(v->get())); 705 | } 706 | #else 707 | if (auto v = std::dynamic_pointer_cast>(shared_from_this())) 708 | return v; 709 | 710 | if (auto v = std::dynamic_pointer_cast>(shared_from_this())) 711 | return make_value(static_cast(v->get())); 712 | #endif 713 | 714 | return nullptr; 715 | } 716 | 717 | template 718 | inline std::shared_ptr> base::as() const 719 | { 720 | #if defined(CPPTOML_NO_RTTI) 721 | if (type() == base_type_traits::type) 722 | return std::static_pointer_cast>(shared_from_this()); 723 | else 724 | return nullptr; 725 | #else 726 | return std::dynamic_pointer_cast>(shared_from_this()); 727 | #endif 728 | } 729 | 730 | // special case value to allow getting an integer parameter as a 731 | // double value 732 | template <> 733 | inline std::shared_ptr> base::as() const 734 | { 735 | #if defined(CPPTOML_NO_RTTI) 736 | if (type() == base_type::FLOAT) 737 | return std::static_pointer_cast>( 738 | shared_from_this()); 739 | 740 | if (type() == base_type::INT) 741 | { 742 | auto v = as(); 743 | // the below has to be a non-const value due to a bug in 744 | // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843 745 | return make_value(static_cast(v->get())); 746 | } 747 | #else 748 | if (auto v 749 | = std::dynamic_pointer_cast>(shared_from_this())) 750 | return v; 751 | 752 | if (auto v = as()) 753 | { 754 | // the below has to be a non-const value due to a bug in 755 | // libc++: https://llvm.org/bugs/show_bug.cgi?id=18843 756 | return make_value(static_cast(v->get())); 757 | } 758 | #endif 759 | 760 | return nullptr; 761 | } 762 | 763 | /** 764 | * Exception class for array insertion errors. 765 | */ 766 | class array_exception : public std::runtime_error 767 | { 768 | public: 769 | array_exception(const std::string& err) : std::runtime_error{err} 770 | { 771 | } 772 | }; 773 | 774 | class array : public base 775 | { 776 | public: 777 | friend std::shared_ptr make_array(); 778 | 779 | std::shared_ptr clone() const override; 780 | 781 | virtual bool is_array() const override 782 | { 783 | return true; 784 | } 785 | 786 | using size_type = std::size_t; 787 | 788 | /** 789 | * arrays can be iterated over 790 | */ 791 | using iterator = std::vector>::iterator; 792 | 793 | /** 794 | * arrays can be iterated over. Const version. 795 | */ 796 | using const_iterator = std::vector>::const_iterator; 797 | 798 | iterator begin() 799 | { 800 | return values_.begin(); 801 | } 802 | 803 | const_iterator begin() const 804 | { 805 | return values_.begin(); 806 | } 807 | 808 | iterator end() 809 | { 810 | return values_.end(); 811 | } 812 | 813 | const_iterator end() const 814 | { 815 | return values_.end(); 816 | } 817 | 818 | /** 819 | * Obtains the array (vector) of base values. 820 | */ 821 | std::vector>& get() 822 | { 823 | return values_; 824 | } 825 | 826 | /** 827 | * Obtains the array (vector) of base values. Const version. 828 | */ 829 | const std::vector>& get() const 830 | { 831 | return values_; 832 | } 833 | 834 | std::shared_ptr at(size_t idx) const 835 | { 836 | return values_.at(idx); 837 | } 838 | 839 | /** 840 | * Obtains an array of values. Note that elements may be 841 | * nullptr if they cannot be converted to a value. 842 | */ 843 | template 844 | std::vector>> array_of() const 845 | { 846 | std::vector>> result(values_.size()); 847 | 848 | std::transform(values_.begin(), values_.end(), result.begin(), 849 | [&](std::shared_ptr v) { return v->as(); }); 850 | 851 | return result; 852 | } 853 | 854 | /** 855 | * Obtains a option>. The option will be empty if the array 856 | * contains values that are not of type T. 857 | */ 858 | template 859 | inline typename array_of_trait::return_type get_array_of() const 860 | { 861 | std::vector result; 862 | result.reserve(values_.size()); 863 | 864 | for (const auto& val : values_) 865 | { 866 | if (auto v = val->as()) 867 | result.push_back(v->get()); 868 | else 869 | return {}; 870 | } 871 | 872 | return {std::move(result)}; 873 | } 874 | 875 | /** 876 | * Obtains an array of arrays. Note that elements may be nullptr 877 | * if they cannot be converted to a array. 878 | */ 879 | std::vector> nested_array() const 880 | { 881 | std::vector> result(values_.size()); 882 | 883 | std::transform(values_.begin(), values_.end(), result.begin(), 884 | [&](std::shared_ptr v) -> std::shared_ptr { 885 | if (v->is_array()) 886 | return std::static_pointer_cast(v); 887 | return std::shared_ptr{}; 888 | }); 889 | 890 | return result; 891 | } 892 | 893 | /** 894 | * Add a value to the end of the array 895 | */ 896 | template 897 | void push_back(const std::shared_ptr>& val) 898 | { 899 | if (values_.empty() || values_[0]->as()) 900 | { 901 | values_.push_back(val); 902 | } 903 | else 904 | { 905 | throw array_exception{"Arrays must be homogenous."}; 906 | } 907 | } 908 | 909 | /** 910 | * Add an array to the end of the array 911 | */ 912 | void push_back(const std::shared_ptr& val) 913 | { 914 | if (values_.empty() || values_[0]->is_array()) 915 | { 916 | values_.push_back(val); 917 | } 918 | else 919 | { 920 | throw array_exception{"Arrays must be homogenous."}; 921 | } 922 | } 923 | 924 | /** 925 | * Convenience function for adding a simple element to the end 926 | * of the array. 927 | */ 928 | template 929 | void push_back(T&& val, typename value_traits::type* = 0) 930 | { 931 | push_back(make_value(std::forward(val))); 932 | } 933 | 934 | /** 935 | * Insert a value into the array 936 | */ 937 | template 938 | iterator insert(iterator position, const std::shared_ptr>& value) 939 | { 940 | if (values_.empty() || values_[0]->as()) 941 | { 942 | return values_.insert(position, value); 943 | } 944 | else 945 | { 946 | throw array_exception{"Arrays must be homogenous."}; 947 | } 948 | } 949 | 950 | /** 951 | * Insert an array into the array 952 | */ 953 | iterator insert(iterator position, const std::shared_ptr& value) 954 | { 955 | if (values_.empty() || values_[0]->is_array()) 956 | { 957 | return values_.insert(position, value); 958 | } 959 | else 960 | { 961 | throw array_exception{"Arrays must be homogenous."}; 962 | } 963 | } 964 | 965 | /** 966 | * Convenience function for inserting a simple element in the array 967 | */ 968 | template 969 | iterator insert(iterator position, T&& val, 970 | typename value_traits::type* = 0) 971 | { 972 | return insert(position, make_value(std::forward(val))); 973 | } 974 | 975 | /** 976 | * Erase an element from the array 977 | */ 978 | iterator erase(iterator position) 979 | { 980 | return values_.erase(position); 981 | } 982 | 983 | /** 984 | * Clear the array 985 | */ 986 | void clear() 987 | { 988 | values_.clear(); 989 | } 990 | 991 | /** 992 | * Reserve space for n values. 993 | */ 994 | void reserve(size_type n) 995 | { 996 | values_.reserve(n); 997 | } 998 | 999 | private: 1000 | #if defined(CPPTOML_NO_RTTI) 1001 | array() : base(base_type::ARRAY) 1002 | { 1003 | // empty 1004 | } 1005 | #else 1006 | array() = default; 1007 | #endif 1008 | 1009 | template 1010 | array(InputIterator begin, InputIterator end) : values_{begin, end} 1011 | { 1012 | // nothing 1013 | } 1014 | 1015 | array(const array& obj) = delete; 1016 | array& operator=(const array& obj) = delete; 1017 | 1018 | std::vector> values_; 1019 | }; 1020 | 1021 | inline std::shared_ptr make_array() 1022 | { 1023 | struct make_shared_enabler : public array 1024 | { 1025 | make_shared_enabler() 1026 | { 1027 | // nothing 1028 | } 1029 | }; 1030 | 1031 | return std::make_shared(); 1032 | } 1033 | 1034 | namespace detail 1035 | { 1036 | template <> 1037 | inline std::shared_ptr make_element() 1038 | { 1039 | return make_array(); 1040 | } 1041 | } // namespace detail 1042 | 1043 | /** 1044 | * Obtains a option>. The option will be empty if the array 1045 | * contains values that are not of type T. 1046 | */ 1047 | template <> 1048 | inline typename array_of_trait::return_type 1049 | array::get_array_of() const 1050 | { 1051 | std::vector> result; 1052 | result.reserve(values_.size()); 1053 | 1054 | for (const auto& val : values_) 1055 | { 1056 | if (auto v = val->as_array()) 1057 | result.push_back(v); 1058 | else 1059 | return {}; 1060 | } 1061 | 1062 | return {std::move(result)}; 1063 | } 1064 | 1065 | class table; 1066 | 1067 | class table_array : public base 1068 | { 1069 | friend class table; 1070 | friend std::shared_ptr make_table_array(bool); 1071 | 1072 | public: 1073 | std::shared_ptr clone() const override; 1074 | 1075 | using size_type = std::size_t; 1076 | 1077 | /** 1078 | * arrays can be iterated over 1079 | */ 1080 | using iterator = std::vector>::iterator; 1081 | 1082 | /** 1083 | * arrays can be iterated over. Const version. 1084 | */ 1085 | using const_iterator = std::vector>::const_iterator; 1086 | 1087 | iterator begin() 1088 | { 1089 | return array_.begin(); 1090 | } 1091 | 1092 | const_iterator begin() const 1093 | { 1094 | return array_.begin(); 1095 | } 1096 | 1097 | iterator end() 1098 | { 1099 | return array_.end(); 1100 | } 1101 | 1102 | const_iterator end() const 1103 | { 1104 | return array_.end(); 1105 | } 1106 | 1107 | virtual bool is_table_array() const override 1108 | { 1109 | return true; 1110 | } 1111 | 1112 | std::vector>& get() 1113 | { 1114 | return array_; 1115 | } 1116 | 1117 | const std::vector>& get() const 1118 | { 1119 | return array_; 1120 | } 1121 | 1122 | /** 1123 | * Add a table to the end of the array 1124 | */ 1125 | void push_back(const std::shared_ptr
& val) 1126 | { 1127 | array_.push_back(val); 1128 | } 1129 | 1130 | /** 1131 | * Insert a table into the array 1132 | */ 1133 | iterator insert(iterator position, const std::shared_ptr
& value) 1134 | { 1135 | return array_.insert(position, value); 1136 | } 1137 | 1138 | /** 1139 | * Erase an element from the array 1140 | */ 1141 | iterator erase(iterator position) 1142 | { 1143 | return array_.erase(position); 1144 | } 1145 | 1146 | /** 1147 | * Clear the array 1148 | */ 1149 | void clear() 1150 | { 1151 | array_.clear(); 1152 | } 1153 | 1154 | /** 1155 | * Reserve space for n tables. 1156 | */ 1157 | void reserve(size_type n) 1158 | { 1159 | array_.reserve(n); 1160 | } 1161 | 1162 | /** 1163 | * Whether or not the table array is declared inline. This mostly 1164 | * matters for parsing, where statically defined arrays cannot be 1165 | * appended to using the array-of-table syntax. 1166 | */ 1167 | bool is_inline() const 1168 | { 1169 | return is_inline_; 1170 | } 1171 | 1172 | private: 1173 | #if defined(CPPTOML_NO_RTTI) 1174 | table_array(bool is_inline = false) 1175 | : base(base_type::TABLE_ARRAY), is_inline_(is_inline) 1176 | { 1177 | // nothing 1178 | } 1179 | #else 1180 | table_array(bool is_inline = false) : is_inline_(is_inline) 1181 | { 1182 | // nothing 1183 | } 1184 | #endif 1185 | 1186 | table_array(const table_array& obj) = delete; 1187 | table_array& operator=(const table_array& rhs) = delete; 1188 | 1189 | std::vector> array_; 1190 | const bool is_inline_ = false; 1191 | }; 1192 | 1193 | inline std::shared_ptr make_table_array(bool is_inline) 1194 | { 1195 | struct make_shared_enabler : public table_array 1196 | { 1197 | make_shared_enabler(bool mse_is_inline) : table_array(mse_is_inline) 1198 | { 1199 | // nothing 1200 | } 1201 | }; 1202 | 1203 | return std::make_shared(is_inline); 1204 | } 1205 | 1206 | namespace detail 1207 | { 1208 | template <> 1209 | inline std::shared_ptr make_element() 1210 | { 1211 | return make_table_array(true); 1212 | } 1213 | } // namespace detail 1214 | 1215 | // The below are overloads for fetching specific value types out of a value 1216 | // where special casting behavior (like bounds checking) is desired 1217 | 1218 | template 1219 | typename std::enable_if::value 1220 | && std::is_signed::value, 1221 | option>::type 1222 | get_impl(const std::shared_ptr& elem) 1223 | { 1224 | if (auto v = elem->as()) 1225 | { 1226 | if (v->get() < (std::numeric_limits::min)()) 1227 | throw std::underflow_error{ 1228 | "T cannot represent the value requested in get"}; 1229 | 1230 | if (v->get() > (std::numeric_limits::max)()) 1231 | throw std::overflow_error{ 1232 | "T cannot represent the value requested in get"}; 1233 | 1234 | return {static_cast(v->get())}; 1235 | } 1236 | else 1237 | { 1238 | return {}; 1239 | } 1240 | } 1241 | 1242 | template 1243 | typename std::enable_if::value 1244 | && std::is_unsigned::value, 1245 | option>::type 1246 | get_impl(const std::shared_ptr& elem) 1247 | { 1248 | if (auto v = elem->as()) 1249 | { 1250 | if (v->get() < 0) 1251 | throw std::underflow_error{"T cannot store negative value in get"}; 1252 | 1253 | if (static_cast(v->get()) > (std::numeric_limits::max)()) 1254 | throw std::overflow_error{ 1255 | "T cannot represent the value requested in get"}; 1256 | 1257 | return {static_cast(v->get())}; 1258 | } 1259 | else 1260 | { 1261 | return {}; 1262 | } 1263 | } 1264 | 1265 | template 1266 | typename std::enable_if::value 1267 | || std::is_same::value, 1268 | option>::type 1269 | get_impl(const std::shared_ptr& elem) 1270 | { 1271 | if (auto v = elem->as()) 1272 | { 1273 | return {v->get()}; 1274 | } 1275 | else 1276 | { 1277 | return {}; 1278 | } 1279 | } 1280 | 1281 | /** 1282 | * Represents a TOML keytable. 1283 | */ 1284 | class table : public base 1285 | { 1286 | public: 1287 | friend class table_array; 1288 | friend std::shared_ptr
make_table(); 1289 | 1290 | std::shared_ptr clone() const override; 1291 | 1292 | /** 1293 | * tables can be iterated over. 1294 | */ 1295 | using iterator = string_to_base_map::iterator; 1296 | 1297 | /** 1298 | * tables can be iterated over. Const version. 1299 | */ 1300 | using const_iterator = string_to_base_map::const_iterator; 1301 | 1302 | iterator begin() 1303 | { 1304 | return map_.begin(); 1305 | } 1306 | 1307 | const_iterator begin() const 1308 | { 1309 | return map_.begin(); 1310 | } 1311 | 1312 | iterator end() 1313 | { 1314 | return map_.end(); 1315 | } 1316 | 1317 | const_iterator end() const 1318 | { 1319 | return map_.end(); 1320 | } 1321 | 1322 | bool is_table() const override 1323 | { 1324 | return true; 1325 | } 1326 | 1327 | bool empty() const 1328 | { 1329 | return map_.empty(); 1330 | } 1331 | 1332 | /** 1333 | * Determines if this key table contains the given key. 1334 | */ 1335 | bool contains(const std::string& key) const 1336 | { 1337 | return map_.find(key) != map_.end(); 1338 | } 1339 | 1340 | /** 1341 | * Determines if this key table contains the given key. Will 1342 | * resolve "qualified keys". Qualified keys are the full access 1343 | * path separated with dots like "grandparent.parent.child". 1344 | */ 1345 | bool contains_qualified(const std::string& key) const 1346 | { 1347 | return resolve_qualified(key); 1348 | } 1349 | 1350 | /** 1351 | * Obtains the base for a given key. 1352 | * @throw std::out_of_range if the key does not exist 1353 | */ 1354 | std::shared_ptr get(const std::string& key) const 1355 | { 1356 | return map_.at(key); 1357 | } 1358 | 1359 | /** 1360 | * Obtains the base for a given key. Will resolve "qualified 1361 | * keys". Qualified keys are the full access path separated with 1362 | * dots like "grandparent.parent.child". 1363 | * 1364 | * @throw std::out_of_range if the key does not exist 1365 | */ 1366 | std::shared_ptr get_qualified(const std::string& key) const 1367 | { 1368 | std::shared_ptr p; 1369 | resolve_qualified(key, &p); 1370 | return p; 1371 | } 1372 | 1373 | /** 1374 | * Obtains a table for a given key, if possible. 1375 | */ 1376 | std::shared_ptr
get_table(const std::string& key) const 1377 | { 1378 | if (contains(key) && get(key)->is_table()) 1379 | return std::static_pointer_cast
(get(key)); 1380 | return nullptr; 1381 | } 1382 | 1383 | /** 1384 | * Obtains a table for a given key, if possible. Will resolve 1385 | * "qualified keys". 1386 | */ 1387 | std::shared_ptr
get_table_qualified(const std::string& key) const 1388 | { 1389 | if (contains_qualified(key) && get_qualified(key)->is_table()) 1390 | return std::static_pointer_cast
(get_qualified(key)); 1391 | return nullptr; 1392 | } 1393 | 1394 | /** 1395 | * Obtains an array for a given key. 1396 | */ 1397 | std::shared_ptr get_array(const std::string& key) const 1398 | { 1399 | if (!contains(key)) 1400 | return nullptr; 1401 | return get(key)->as_array(); 1402 | } 1403 | 1404 | /** 1405 | * Obtains an array for a given key. Will resolve "qualified keys". 1406 | */ 1407 | std::shared_ptr get_array_qualified(const std::string& key) const 1408 | { 1409 | if (!contains_qualified(key)) 1410 | return nullptr; 1411 | return get_qualified(key)->as_array(); 1412 | } 1413 | 1414 | /** 1415 | * Obtains a table_array for a given key, if possible. 1416 | */ 1417 | std::shared_ptr get_table_array(const std::string& key) const 1418 | { 1419 | if (!contains(key)) 1420 | return nullptr; 1421 | return get(key)->as_table_array(); 1422 | } 1423 | 1424 | /** 1425 | * Obtains a table_array for a given key, if possible. Will resolve 1426 | * "qualified keys". 1427 | */ 1428 | std::shared_ptr 1429 | get_table_array_qualified(const std::string& key) const 1430 | { 1431 | if (!contains_qualified(key)) 1432 | return nullptr; 1433 | return get_qualified(key)->as_table_array(); 1434 | } 1435 | 1436 | /** 1437 | * Helper function that attempts to get a value corresponding 1438 | * to the template parameter from a given key. 1439 | */ 1440 | template 1441 | option get_as(const std::string& key) const 1442 | { 1443 | try 1444 | { 1445 | return get_impl(get(key)); 1446 | } 1447 | catch (const std::out_of_range&) 1448 | { 1449 | return {}; 1450 | } 1451 | } 1452 | 1453 | /** 1454 | * Helper function that attempts to get a value corresponding 1455 | * to the template parameter from a given key. Will resolve "qualified 1456 | * keys". 1457 | */ 1458 | template 1459 | option get_qualified_as(const std::string& key) const 1460 | { 1461 | try 1462 | { 1463 | return get_impl(get_qualified(key)); 1464 | } 1465 | catch (const std::out_of_range&) 1466 | { 1467 | return {}; 1468 | } 1469 | } 1470 | 1471 | /** 1472 | * Helper function that attempts to get an array of values of a given 1473 | * type corresponding to the template parameter for a given key. 1474 | * 1475 | * If the key doesn't exist, doesn't exist as an array type, or one or 1476 | * more keys inside the array type are not of type T, an empty option 1477 | * is returned. Otherwise, an option containing a vector of the values 1478 | * is returned. 1479 | */ 1480 | template 1481 | inline typename array_of_trait::return_type 1482 | get_array_of(const std::string& key) const 1483 | { 1484 | if (auto v = get_array(key)) 1485 | { 1486 | std::vector result; 1487 | result.reserve(v->get().size()); 1488 | 1489 | for (const auto& b : v->get()) 1490 | { 1491 | if (auto val = b->as()) 1492 | result.push_back(val->get()); 1493 | else 1494 | return {}; 1495 | } 1496 | return {std::move(result)}; 1497 | } 1498 | 1499 | return {}; 1500 | } 1501 | 1502 | /** 1503 | * Helper function that attempts to get an array of values of a given 1504 | * type corresponding to the template parameter for a given key. Will 1505 | * resolve "qualified keys". 1506 | * 1507 | * If the key doesn't exist, doesn't exist as an array type, or one or 1508 | * more keys inside the array type are not of type T, an empty option 1509 | * is returned. Otherwise, an option containing a vector of the values 1510 | * is returned. 1511 | */ 1512 | template 1513 | inline typename array_of_trait::return_type 1514 | get_qualified_array_of(const std::string& key) const 1515 | { 1516 | if (auto v = get_array_qualified(key)) 1517 | { 1518 | std::vector result; 1519 | result.reserve(v->get().size()); 1520 | 1521 | for (const auto& b : v->get()) 1522 | { 1523 | if (auto val = b->as()) 1524 | result.push_back(val->get()); 1525 | else 1526 | return {}; 1527 | } 1528 | return {std::move(result)}; 1529 | } 1530 | 1531 | return {}; 1532 | } 1533 | 1534 | /** 1535 | * Adds an element to the keytable. 1536 | */ 1537 | void insert(const std::string& key, const std::shared_ptr& value) 1538 | { 1539 | map_[key] = value; 1540 | } 1541 | 1542 | /** 1543 | * Convenience shorthand for adding a simple element to the 1544 | * keytable. 1545 | */ 1546 | template 1547 | void insert(const std::string& key, T&& val, 1548 | typename value_traits::type* = 0) 1549 | { 1550 | insert(key, make_value(std::forward(val))); 1551 | } 1552 | 1553 | /** 1554 | * Removes an element from the table. 1555 | */ 1556 | void erase(const std::string& key) 1557 | { 1558 | map_.erase(key); 1559 | } 1560 | 1561 | private: 1562 | #if defined(CPPTOML_NO_RTTI) 1563 | table() : base(base_type::TABLE) 1564 | { 1565 | // nothing 1566 | } 1567 | #else 1568 | table() 1569 | { 1570 | // nothing 1571 | } 1572 | #endif 1573 | 1574 | table(const table& obj) = delete; 1575 | table& operator=(const table& rhs) = delete; 1576 | 1577 | std::vector split(const std::string& value, 1578 | char separator) const 1579 | { 1580 | std::vector result; 1581 | std::string::size_type p = 0; 1582 | std::string::size_type q; 1583 | while ((q = value.find(separator, p)) != std::string::npos) 1584 | { 1585 | result.emplace_back(value, p, q - p); 1586 | p = q + 1; 1587 | } 1588 | result.emplace_back(value, p); 1589 | return result; 1590 | } 1591 | 1592 | // If output parameter p is specified, fill it with the pointer to the 1593 | // specified entry and throw std::out_of_range if it couldn't be found. 1594 | // 1595 | // Otherwise, just return true if the entry could be found or false 1596 | // otherwise and do not throw. 1597 | bool resolve_qualified(const std::string& key, 1598 | std::shared_ptr* p = nullptr) const 1599 | { 1600 | auto parts = split(key, '.'); 1601 | auto last_key = parts.back(); 1602 | parts.pop_back(); 1603 | 1604 | auto cur_table = this; 1605 | for (const auto& part : parts) 1606 | { 1607 | cur_table = cur_table->get_table(part).get(); 1608 | if (!cur_table) 1609 | { 1610 | if (!p) 1611 | return false; 1612 | 1613 | throw std::out_of_range{key + " is not a valid key"}; 1614 | } 1615 | } 1616 | 1617 | if (!p) 1618 | return cur_table->map_.count(last_key) != 0; 1619 | 1620 | *p = cur_table->map_.at(last_key); 1621 | return true; 1622 | } 1623 | 1624 | string_to_base_map map_; 1625 | }; 1626 | 1627 | /** 1628 | * Helper function that attempts to get an array of arrays for a given 1629 | * key. 1630 | * 1631 | * If the key doesn't exist, doesn't exist as an array type, or one or 1632 | * more keys inside the array type are not of type T, an empty option 1633 | * is returned. Otherwise, an option containing a vector of the values 1634 | * is returned. 1635 | */ 1636 | template <> 1637 | inline typename array_of_trait::return_type 1638 | table::get_array_of(const std::string& key) const 1639 | { 1640 | if (auto v = get_array(key)) 1641 | { 1642 | std::vector> result; 1643 | result.reserve(v->get().size()); 1644 | 1645 | for (const auto& b : v->get()) 1646 | { 1647 | if (auto val = b->as_array()) 1648 | result.push_back(val); 1649 | else 1650 | return {}; 1651 | } 1652 | 1653 | return {std::move(result)}; 1654 | } 1655 | 1656 | return {}; 1657 | } 1658 | 1659 | /** 1660 | * Helper function that attempts to get an array of arrays for a given 1661 | * key. Will resolve "qualified keys". 1662 | * 1663 | * If the key doesn't exist, doesn't exist as an array type, or one or 1664 | * more keys inside the array type are not of type T, an empty option 1665 | * is returned. Otherwise, an option containing a vector of the values 1666 | * is returned. 1667 | */ 1668 | template <> 1669 | inline typename array_of_trait::return_type 1670 | table::get_qualified_array_of(const std::string& key) const 1671 | { 1672 | if (auto v = get_array_qualified(key)) 1673 | { 1674 | std::vector> result; 1675 | result.reserve(v->get().size()); 1676 | 1677 | for (const auto& b : v->get()) 1678 | { 1679 | if (auto val = b->as_array()) 1680 | result.push_back(val); 1681 | else 1682 | return {}; 1683 | } 1684 | 1685 | return {std::move(result)}; 1686 | } 1687 | 1688 | return {}; 1689 | } 1690 | 1691 | std::shared_ptr
make_table() 1692 | { 1693 | struct make_shared_enabler : public table 1694 | { 1695 | make_shared_enabler() 1696 | { 1697 | // nothing 1698 | } 1699 | }; 1700 | 1701 | return std::make_shared(); 1702 | } 1703 | 1704 | namespace detail 1705 | { 1706 | template <> 1707 | inline std::shared_ptr
make_element
() 1708 | { 1709 | return make_table(); 1710 | } 1711 | } // namespace detail 1712 | 1713 | template 1714 | std::shared_ptr value::clone() const 1715 | { 1716 | return make_value(data_); 1717 | } 1718 | 1719 | inline std::shared_ptr array::clone() const 1720 | { 1721 | auto result = make_array(); 1722 | result->reserve(values_.size()); 1723 | for (const auto& ptr : values_) 1724 | result->values_.push_back(ptr->clone()); 1725 | return result; 1726 | } 1727 | 1728 | inline std::shared_ptr table_array::clone() const 1729 | { 1730 | auto result = make_table_array(is_inline()); 1731 | result->reserve(array_.size()); 1732 | for (const auto& ptr : array_) 1733 | result->array_.push_back(ptr->clone()->as_table()); 1734 | return result; 1735 | } 1736 | 1737 | inline std::shared_ptr table::clone() const 1738 | { 1739 | auto result = make_table(); 1740 | for (const auto& pr : map_) 1741 | result->insert(pr.first, pr.second->clone()); 1742 | return result; 1743 | } 1744 | 1745 | /** 1746 | * Exception class for all TOML parsing errors. 1747 | */ 1748 | class parse_exception : public std::runtime_error 1749 | { 1750 | public: 1751 | parse_exception(const std::string& err) : std::runtime_error{err} 1752 | { 1753 | } 1754 | 1755 | parse_exception(const std::string& err, std::size_t line_number) 1756 | : std::runtime_error{err + " at line " + std::to_string(line_number)} 1757 | { 1758 | } 1759 | }; 1760 | 1761 | inline bool is_number(char c) 1762 | { 1763 | return c >= '0' && c <= '9'; 1764 | } 1765 | 1766 | inline bool is_hex(char c) 1767 | { 1768 | return is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); 1769 | } 1770 | 1771 | /** 1772 | * Helper object for consuming expected characters. 1773 | */ 1774 | template 1775 | class consumer 1776 | { 1777 | public: 1778 | consumer(std::string::iterator& it, const std::string::iterator& end, 1779 | OnError&& on_error) 1780 | : it_(it), end_(end), on_error_(std::forward(on_error)) 1781 | { 1782 | // nothing 1783 | } 1784 | 1785 | void operator()(char c) 1786 | { 1787 | if (it_ == end_ || *it_ != c) 1788 | on_error_(); 1789 | ++it_; 1790 | } 1791 | 1792 | template 1793 | void operator()(const char (&str)[N]) 1794 | { 1795 | std::for_each(std::begin(str), std::end(str) - 1, 1796 | [&](char c) { (*this)(c); }); 1797 | } 1798 | 1799 | void eat_or(char a, char b) 1800 | { 1801 | if (it_ == end_ || (*it_ != a && *it_ != b)) 1802 | on_error_(); 1803 | ++it_; 1804 | } 1805 | 1806 | int eat_digits(int len) 1807 | { 1808 | int val = 0; 1809 | for (int i = 0; i < len; ++i) 1810 | { 1811 | if (!is_number(*it_) || it_ == end_) 1812 | on_error_(); 1813 | val = 10 * val + (*it_++ - '0'); 1814 | } 1815 | return val; 1816 | } 1817 | 1818 | void error() 1819 | { 1820 | on_error_(); 1821 | } 1822 | 1823 | private: 1824 | std::string::iterator& it_; 1825 | const std::string::iterator& end_; 1826 | OnError on_error_; 1827 | }; 1828 | 1829 | template 1830 | consumer make_consumer(std::string::iterator& it, 1831 | const std::string::iterator& end, 1832 | OnError&& on_error) 1833 | { 1834 | return consumer(it, end, std::forward(on_error)); 1835 | } 1836 | 1837 | // replacement for std::getline to handle incorrectly line-ended files 1838 | // https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf 1839 | namespace detail 1840 | { 1841 | inline std::istream& getline(std::istream& input, std::string& line) 1842 | { 1843 | line.clear(); 1844 | 1845 | std::istream::sentry sentry{input, true}; 1846 | auto sb = input.rdbuf(); 1847 | 1848 | while (true) 1849 | { 1850 | auto c = sb->sbumpc(); 1851 | if (c == '\r') 1852 | { 1853 | if (sb->sgetc() == '\n') 1854 | c = sb->sbumpc(); 1855 | } 1856 | 1857 | if (c == '\n') 1858 | return input; 1859 | 1860 | if (c == std::istream::traits_type::eof()) 1861 | { 1862 | if (line.empty()) 1863 | input.setstate(std::ios::eofbit); 1864 | return input; 1865 | } 1866 | 1867 | line.push_back(static_cast(c)); 1868 | } 1869 | } 1870 | } // namespace detail 1871 | 1872 | /** 1873 | * The parser class. 1874 | */ 1875 | class parser 1876 | { 1877 | public: 1878 | /** 1879 | * Parsers are constructed from streams. 1880 | */ 1881 | parser(std::istream& stream) : input_(stream) 1882 | { 1883 | // nothing 1884 | } 1885 | 1886 | parser& operator=(const parser& parser) = delete; 1887 | 1888 | /** 1889 | * Parses the stream this parser was created on until EOF. 1890 | * @throw parse_exception if there are errors in parsing 1891 | */ 1892 | std::shared_ptr
parse() 1893 | { 1894 | std::shared_ptr
root = make_table(); 1895 | 1896 | table* curr_table = root.get(); 1897 | 1898 | while (detail::getline(input_, line_)) 1899 | { 1900 | line_number_++; 1901 | auto it = line_.begin(); 1902 | auto end = line_.end(); 1903 | consume_whitespace(it, end); 1904 | if (it == end || *it == '#') 1905 | continue; 1906 | if (*it == '[') 1907 | { 1908 | curr_table = root.get(); 1909 | parse_table(it, end, curr_table); 1910 | } 1911 | else 1912 | { 1913 | parse_key_value(it, end, curr_table); 1914 | consume_whitespace(it, end); 1915 | eol_or_comment(it, end); 1916 | } 1917 | } 1918 | return root; 1919 | } 1920 | 1921 | private: 1922 | #if defined _MSC_VER 1923 | __declspec(noreturn) 1924 | #elif defined __GNUC__ 1925 | __attribute__((noreturn)) 1926 | #endif 1927 | void throw_parse_exception(const std::string& err) 1928 | { 1929 | throw parse_exception{err, line_number_}; 1930 | } 1931 | 1932 | void parse_table(std::string::iterator& it, 1933 | const std::string::iterator& end, table*& curr_table) 1934 | { 1935 | // remove the beginning keytable marker 1936 | ++it; 1937 | if (it == end) 1938 | throw_parse_exception("Unexpected end of table"); 1939 | if (*it == '[') 1940 | parse_table_array(it, end, curr_table); 1941 | else 1942 | parse_single_table(it, end, curr_table); 1943 | } 1944 | 1945 | void parse_single_table(std::string::iterator& it, 1946 | const std::string::iterator& end, 1947 | table*& curr_table) 1948 | { 1949 | if (it == end || *it == ']') 1950 | throw_parse_exception("Table name cannot be empty"); 1951 | 1952 | std::string full_table_name; 1953 | bool inserted = false; 1954 | 1955 | auto key_end = [](char c) { return c == ']'; }; 1956 | 1957 | auto key_part_handler = [&](const std::string& part) { 1958 | if (part.empty()) 1959 | throw_parse_exception("Empty component of table name"); 1960 | 1961 | if (!full_table_name.empty()) 1962 | full_table_name += '.'; 1963 | full_table_name += part; 1964 | 1965 | if (curr_table->contains(part)) 1966 | { 1967 | #if !defined(__PGI) 1968 | auto b = curr_table->get(part); 1969 | #else 1970 | // Workaround for PGI compiler 1971 | std::shared_ptr b = curr_table->get(part); 1972 | #endif 1973 | if (b->is_table()) 1974 | curr_table = static_cast(b.get()); 1975 | else if (b->is_table_array()) 1976 | curr_table = std::static_pointer_cast(b) 1977 | ->get() 1978 | .back() 1979 | .get(); 1980 | else 1981 | throw_parse_exception("Key " + full_table_name 1982 | + "already exists as a value"); 1983 | } 1984 | else 1985 | { 1986 | inserted = true; 1987 | curr_table->insert(part, make_table()); 1988 | curr_table = static_cast(curr_table->get(part).get()); 1989 | } 1990 | }; 1991 | 1992 | key_part_handler(parse_key(it, end, key_end, key_part_handler)); 1993 | 1994 | if (it == end) 1995 | throw_parse_exception( 1996 | "Unterminated table declaration; did you forget a ']'?"); 1997 | 1998 | if (*it != ']') 1999 | { 2000 | std::string errmsg{"Unexpected character in table definition: "}; 2001 | errmsg += '"'; 2002 | errmsg += *it; 2003 | errmsg += '"'; 2004 | throw_parse_exception(errmsg); 2005 | } 2006 | 2007 | // table already existed 2008 | if (!inserted) 2009 | { 2010 | auto is_value 2011 | = [](const std::pair&>& p) { 2013 | return p.second->is_value(); 2014 | }; 2015 | 2016 | // if there are any values, we can't add values to this table 2017 | // since it has already been defined. If there aren't any 2018 | // values, then it was implicitly created by something like 2019 | // [a.b] 2020 | if (curr_table->empty() 2021 | || std::any_of(curr_table->begin(), curr_table->end(), 2022 | is_value)) 2023 | { 2024 | throw_parse_exception("Redefinition of table " 2025 | + full_table_name); 2026 | } 2027 | } 2028 | 2029 | ++it; 2030 | consume_whitespace(it, end); 2031 | eol_or_comment(it, end); 2032 | } 2033 | 2034 | void parse_table_array(std::string::iterator& it, 2035 | const std::string::iterator& end, table*& curr_table) 2036 | { 2037 | ++it; 2038 | if (it == end || *it == ']') 2039 | throw_parse_exception("Table array name cannot be empty"); 2040 | 2041 | auto key_end = [](char c) { return c == ']'; }; 2042 | 2043 | std::string full_ta_name; 2044 | auto key_part_handler = [&](const std::string& part) { 2045 | if (part.empty()) 2046 | throw_parse_exception("Empty component of table array name"); 2047 | 2048 | if (!full_ta_name.empty()) 2049 | full_ta_name += '.'; 2050 | full_ta_name += part; 2051 | 2052 | if (curr_table->contains(part)) 2053 | { 2054 | #if !defined(__PGI) 2055 | auto b = curr_table->get(part); 2056 | #else 2057 | // Workaround for PGI compiler 2058 | std::shared_ptr b = curr_table->get(part); 2059 | #endif 2060 | 2061 | // if this is the end of the table array name, add an 2062 | // element to the table array that we just looked up, 2063 | // provided it was not declared inline 2064 | if (it != end && *it == ']') 2065 | { 2066 | if (!b->is_table_array()) 2067 | { 2068 | throw_parse_exception("Key " + full_ta_name 2069 | + " is not a table array"); 2070 | } 2071 | 2072 | auto v = b->as_table_array(); 2073 | 2074 | if (v->is_inline()) 2075 | { 2076 | throw_parse_exception("Static array " + full_ta_name 2077 | + " cannot be appended to"); 2078 | } 2079 | 2080 | v->get().push_back(make_table()); 2081 | curr_table = v->get().back().get(); 2082 | } 2083 | // otherwise, just keep traversing down the key name 2084 | else 2085 | { 2086 | if (b->is_table()) 2087 | curr_table = static_cast(b.get()); 2088 | else if (b->is_table_array()) 2089 | curr_table = std::static_pointer_cast(b) 2090 | ->get() 2091 | .back() 2092 | .get(); 2093 | else 2094 | throw_parse_exception("Key " + full_ta_name 2095 | + " already exists as a value"); 2096 | } 2097 | } 2098 | else 2099 | { 2100 | // if this is the end of the table array name, add a new 2101 | // table array and a new table inside that array for us to 2102 | // add keys to next 2103 | if (it != end && *it == ']') 2104 | { 2105 | curr_table->insert(part, make_table_array()); 2106 | auto arr = std::static_pointer_cast( 2107 | curr_table->get(part)); 2108 | arr->get().push_back(make_table()); 2109 | curr_table = arr->get().back().get(); 2110 | } 2111 | // otherwise, create the implicitly defined table and move 2112 | // down to it 2113 | else 2114 | { 2115 | curr_table->insert(part, make_table()); 2116 | curr_table 2117 | = static_cast(curr_table->get(part).get()); 2118 | } 2119 | } 2120 | }; 2121 | 2122 | key_part_handler(parse_key(it, end, key_end, key_part_handler)); 2123 | 2124 | // consume the last "]]" 2125 | auto eat = make_consumer(it, end, [this]() { 2126 | throw_parse_exception("Unterminated table array name"); 2127 | }); 2128 | eat(']'); 2129 | eat(']'); 2130 | 2131 | consume_whitespace(it, end); 2132 | eol_or_comment(it, end); 2133 | } 2134 | 2135 | void parse_key_value(std::string::iterator& it, std::string::iterator& end, 2136 | table* curr_table) 2137 | { 2138 | auto key_end = [](char c) { return c == '='; }; 2139 | 2140 | auto key_part_handler = [&](const std::string& part) { 2141 | // two cases: this key part exists already, in which case it must 2142 | // be a table, or it doesn't exist in which case we must create 2143 | // an implicitly defined table 2144 | if (curr_table->contains(part)) 2145 | { 2146 | auto val = curr_table->get(part); 2147 | if (val->is_table()) 2148 | { 2149 | curr_table = static_cast(val.get()); 2150 | } 2151 | else 2152 | { 2153 | throw_parse_exception("Key " + part 2154 | + " already exists as a value"); 2155 | } 2156 | } 2157 | else 2158 | { 2159 | auto newtable = make_table(); 2160 | curr_table->insert(part, newtable); 2161 | curr_table = newtable.get(); 2162 | } 2163 | }; 2164 | 2165 | auto key = parse_key(it, end, key_end, key_part_handler); 2166 | 2167 | if (curr_table->contains(key)) 2168 | throw_parse_exception("Key " + key + " already present"); 2169 | if (it == end || *it != '=') 2170 | throw_parse_exception("Value must follow after a '='"); 2171 | ++it; 2172 | consume_whitespace(it, end); 2173 | curr_table->insert(key, parse_value(it, end)); 2174 | consume_whitespace(it, end); 2175 | } 2176 | 2177 | template 2178 | std::string 2179 | parse_key(std::string::iterator& it, const std::string::iterator& end, 2180 | KeyEndFinder&& key_end, KeyPartHandler&& key_part_handler) 2181 | { 2182 | // parse the key as a series of one or more simple-keys joined with '.' 2183 | while (it != end && !key_end(*it)) 2184 | { 2185 | auto part = parse_simple_key(it, end); 2186 | consume_whitespace(it, end); 2187 | 2188 | if (it == end || key_end(*it)) 2189 | { 2190 | return part; 2191 | } 2192 | 2193 | if (*it != '.') 2194 | { 2195 | std::string errmsg{"Unexpected character in key: "}; 2196 | errmsg += '"'; 2197 | errmsg += *it; 2198 | errmsg += '"'; 2199 | throw_parse_exception(errmsg); 2200 | } 2201 | 2202 | key_part_handler(part); 2203 | 2204 | // consume the dot 2205 | ++it; 2206 | } 2207 | 2208 | throw_parse_exception("Unexpected end of key"); 2209 | } 2210 | 2211 | std::string parse_simple_key(std::string::iterator& it, 2212 | const std::string::iterator& end) 2213 | { 2214 | consume_whitespace(it, end); 2215 | 2216 | if (it == end) 2217 | throw_parse_exception("Unexpected end of key (blank key?)"); 2218 | 2219 | if (*it == '"' || *it == '\'') 2220 | { 2221 | return string_literal(it, end, *it); 2222 | } 2223 | else 2224 | { 2225 | auto bke = std::find_if(it, end, [](char c) { 2226 | return c == '.' || c == '=' || c == ']'; 2227 | }); 2228 | return parse_bare_key(it, bke); 2229 | } 2230 | } 2231 | 2232 | std::string parse_bare_key(std::string::iterator& it, 2233 | const std::string::iterator& end) 2234 | { 2235 | if (it == end) 2236 | { 2237 | throw_parse_exception("Bare key missing name"); 2238 | } 2239 | 2240 | auto key_end = end; 2241 | --key_end; 2242 | consume_backwards_whitespace(key_end, it); 2243 | ++key_end; 2244 | std::string key{it, key_end}; 2245 | 2246 | if (std::find(it, key_end, '#') != key_end) 2247 | { 2248 | throw_parse_exception("Bare key " + key + " cannot contain #"); 2249 | } 2250 | 2251 | if (std::find_if(it, key_end, 2252 | [](char c) { return c == ' ' || c == '\t'; }) 2253 | != key_end) 2254 | { 2255 | throw_parse_exception("Bare key " + key 2256 | + " cannot contain whitespace"); 2257 | } 2258 | 2259 | if (std::find_if(it, key_end, 2260 | [](char c) { return c == '[' || c == ']'; }) 2261 | != key_end) 2262 | { 2263 | throw_parse_exception("Bare key " + key 2264 | + " cannot contain '[' or ']'"); 2265 | } 2266 | 2267 | it = end; 2268 | return key; 2269 | } 2270 | 2271 | enum class parse_type 2272 | { 2273 | STRING = 1, 2274 | LOCAL_TIME, 2275 | LOCAL_DATE, 2276 | LOCAL_DATETIME, 2277 | OFFSET_DATETIME, 2278 | INT, 2279 | FLOAT, 2280 | BOOL, 2281 | ARRAY, 2282 | INLINE_TABLE 2283 | }; 2284 | 2285 | std::shared_ptr parse_value(std::string::iterator& it, 2286 | std::string::iterator& end) 2287 | { 2288 | parse_type type = determine_value_type(it, end); 2289 | switch (type) 2290 | { 2291 | case parse_type::STRING: 2292 | return parse_string(it, end); 2293 | case parse_type::LOCAL_TIME: 2294 | return parse_time(it, end); 2295 | case parse_type::LOCAL_DATE: 2296 | case parse_type::LOCAL_DATETIME: 2297 | case parse_type::OFFSET_DATETIME: 2298 | return parse_date(it, end); 2299 | case parse_type::INT: 2300 | case parse_type::FLOAT: 2301 | return parse_number(it, end); 2302 | case parse_type::BOOL: 2303 | return parse_bool(it, end); 2304 | case parse_type::ARRAY: 2305 | return parse_array(it, end); 2306 | case parse_type::INLINE_TABLE: 2307 | return parse_inline_table(it, end); 2308 | default: 2309 | throw_parse_exception("Failed to parse value"); 2310 | } 2311 | } 2312 | 2313 | parse_type determine_value_type(const std::string::iterator& it, 2314 | const std::string::iterator& end) 2315 | { 2316 | if (it == end) 2317 | { 2318 | throw_parse_exception("Failed to parse value type"); 2319 | } 2320 | if (*it == '"' || *it == '\'') 2321 | { 2322 | return parse_type::STRING; 2323 | } 2324 | else if (is_time(it, end)) 2325 | { 2326 | return parse_type::LOCAL_TIME; 2327 | } 2328 | else if (auto dtype = date_type(it, end)) 2329 | { 2330 | return *dtype; 2331 | } 2332 | else if (is_number(*it) || *it == '-' || *it == '+' 2333 | || (*it == 'i' && it + 1 != end && it[1] == 'n' 2334 | && it + 2 != end && it[2] == 'f') 2335 | || (*it == 'n' && it + 1 != end && it[1] == 'a' 2336 | && it + 2 != end && it[2] == 'n')) 2337 | { 2338 | return determine_number_type(it, end); 2339 | } 2340 | else if (*it == 't' || *it == 'f') 2341 | { 2342 | return parse_type::BOOL; 2343 | } 2344 | else if (*it == '[') 2345 | { 2346 | return parse_type::ARRAY; 2347 | } 2348 | else if (*it == '{') 2349 | { 2350 | return parse_type::INLINE_TABLE; 2351 | } 2352 | throw_parse_exception("Failed to parse value type"); 2353 | } 2354 | 2355 | parse_type determine_number_type(const std::string::iterator& it, 2356 | const std::string::iterator& end) 2357 | { 2358 | // determine if we are an integer or a float 2359 | auto check_it = it; 2360 | if (*check_it == '-' || *check_it == '+') 2361 | ++check_it; 2362 | 2363 | if (check_it == end) 2364 | throw_parse_exception("Malformed number"); 2365 | 2366 | if (*check_it == 'i' || *check_it == 'n') 2367 | return parse_type::FLOAT; 2368 | 2369 | while (check_it != end && is_number(*check_it)) 2370 | ++check_it; 2371 | if (check_it != end && *check_it == '.') 2372 | { 2373 | ++check_it; 2374 | while (check_it != end && is_number(*check_it)) 2375 | ++check_it; 2376 | return parse_type::FLOAT; 2377 | } 2378 | else 2379 | { 2380 | return parse_type::INT; 2381 | } 2382 | } 2383 | 2384 | std::shared_ptr> parse_string(std::string::iterator& it, 2385 | std::string::iterator& end) 2386 | { 2387 | auto delim = *it; 2388 | assert(delim == '"' || delim == '\''); 2389 | 2390 | // end is non-const here because we have to be able to potentially 2391 | // parse multiple lines in a string, not just one 2392 | auto check_it = it; 2393 | ++check_it; 2394 | if (check_it != end && *check_it == delim) 2395 | { 2396 | ++check_it; 2397 | if (check_it != end && *check_it == delim) 2398 | { 2399 | it = ++check_it; 2400 | return parse_multiline_string(it, end, delim); 2401 | } 2402 | } 2403 | return make_value(string_literal(it, end, delim)); 2404 | } 2405 | 2406 | std::shared_ptr> 2407 | parse_multiline_string(std::string::iterator& it, 2408 | std::string::iterator& end, char delim) 2409 | { 2410 | std::stringstream ss; 2411 | 2412 | auto is_ws = [](char c) { return c == ' ' || c == '\t'; }; 2413 | 2414 | bool consuming = false; 2415 | std::shared_ptr> ret; 2416 | 2417 | auto handle_line = [&](std::string::iterator& local_it, 2418 | std::string::iterator& local_end) { 2419 | if (consuming) 2420 | { 2421 | local_it = std::find_if_not(local_it, local_end, is_ws); 2422 | 2423 | // whole line is whitespace 2424 | if (local_it == local_end) 2425 | return; 2426 | } 2427 | 2428 | consuming = false; 2429 | 2430 | while (local_it != local_end) 2431 | { 2432 | // handle escaped characters 2433 | if (delim == '"' && *local_it == '\\') 2434 | { 2435 | auto check = local_it; 2436 | // check if this is an actual escape sequence or a 2437 | // whitespace escaping backslash 2438 | ++check; 2439 | consume_whitespace(check, local_end); 2440 | if (check == local_end) 2441 | { 2442 | consuming = true; 2443 | break; 2444 | } 2445 | 2446 | ss << parse_escape_code(local_it, local_end); 2447 | continue; 2448 | } 2449 | 2450 | // if we can end the string 2451 | if (std::distance(local_it, local_end) >= 3) 2452 | { 2453 | auto check = local_it; 2454 | // check for """ 2455 | if (*check++ == delim && *check++ == delim 2456 | && *check++ == delim) 2457 | { 2458 | local_it = check; 2459 | ret = make_value(ss.str()); 2460 | break; 2461 | } 2462 | } 2463 | 2464 | ss << *local_it++; 2465 | } 2466 | }; 2467 | 2468 | // handle the remainder of the current line 2469 | handle_line(it, end); 2470 | if (ret) 2471 | return ret; 2472 | 2473 | // start eating lines 2474 | while (detail::getline(input_, line_)) 2475 | { 2476 | ++line_number_; 2477 | 2478 | it = line_.begin(); 2479 | end = line_.end(); 2480 | 2481 | handle_line(it, end); 2482 | 2483 | if (ret) 2484 | return ret; 2485 | 2486 | if (!consuming) 2487 | ss << std::endl; 2488 | } 2489 | 2490 | throw_parse_exception("Unterminated multi-line basic string"); 2491 | } 2492 | 2493 | std::string string_literal(std::string::iterator& it, 2494 | const std::string::iterator& end, char delim) 2495 | { 2496 | ++it; 2497 | std::string val; 2498 | while (it != end) 2499 | { 2500 | // handle escaped characters 2501 | if (delim == '"' && *it == '\\') 2502 | { 2503 | val += parse_escape_code(it, end); 2504 | } 2505 | else if (*it == delim) 2506 | { 2507 | ++it; 2508 | consume_whitespace(it, end); 2509 | return val; 2510 | } 2511 | else 2512 | { 2513 | val += *it++; 2514 | } 2515 | } 2516 | throw_parse_exception("Unterminated string literal"); 2517 | } 2518 | 2519 | std::string parse_escape_code(std::string::iterator& it, 2520 | const std::string::iterator& end) 2521 | { 2522 | ++it; 2523 | if (it == end) 2524 | throw_parse_exception("Invalid escape sequence"); 2525 | char value; 2526 | if (*it == 'b') 2527 | { 2528 | value = '\b'; 2529 | } 2530 | else if (*it == 't') 2531 | { 2532 | value = '\t'; 2533 | } 2534 | else if (*it == 'n') 2535 | { 2536 | value = '\n'; 2537 | } 2538 | else if (*it == 'f') 2539 | { 2540 | value = '\f'; 2541 | } 2542 | else if (*it == 'r') 2543 | { 2544 | value = '\r'; 2545 | } 2546 | else if (*it == '"') 2547 | { 2548 | value = '"'; 2549 | } 2550 | else if (*it == '\\') 2551 | { 2552 | value = '\\'; 2553 | } 2554 | else if (*it == 'u' || *it == 'U') 2555 | { 2556 | return parse_unicode(it, end); 2557 | } 2558 | else 2559 | { 2560 | throw_parse_exception("Invalid escape sequence"); 2561 | } 2562 | ++it; 2563 | return std::string(1, value); 2564 | } 2565 | 2566 | std::string parse_unicode(std::string::iterator& it, 2567 | const std::string::iterator& end) 2568 | { 2569 | bool large = *it++ == 'U'; 2570 | auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000); 2571 | 2572 | if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff) 2573 | { 2574 | throw_parse_exception( 2575 | "Unicode escape sequence is not a Unicode scalar value"); 2576 | } 2577 | 2578 | std::string result; 2579 | // See Table 3-6 of the Unicode standard 2580 | if (codepoint <= 0x7f) 2581 | { 2582 | // 1-byte codepoints: 00000000 0xxxxxxx 2583 | // repr: 0xxxxxxx 2584 | result += static_cast(codepoint & 0x7f); 2585 | } 2586 | else if (codepoint <= 0x7ff) 2587 | { 2588 | // 2-byte codepoints: 00000yyy yyxxxxxx 2589 | // repr: 110yyyyy 10xxxxxx 2590 | // 2591 | // 0x1f = 00011111 2592 | // 0xc0 = 11000000 2593 | // 2594 | result += static_cast(0xc0 | ((codepoint >> 6) & 0x1f)); 2595 | // 2596 | // 0x80 = 10000000 2597 | // 0x3f = 00111111 2598 | // 2599 | result += static_cast(0x80 | (codepoint & 0x3f)); 2600 | } 2601 | else if (codepoint <= 0xffff) 2602 | { 2603 | // 3-byte codepoints: zzzzyyyy yyxxxxxx 2604 | // repr: 1110zzzz 10yyyyyy 10xxxxxx 2605 | // 2606 | // 0xe0 = 11100000 2607 | // 0x0f = 00001111 2608 | // 2609 | result += static_cast(0xe0 | ((codepoint >> 12) & 0x0f)); 2610 | result += static_cast(0x80 | ((codepoint >> 6) & 0x1f)); 2611 | result += static_cast(0x80 | (codepoint & 0x3f)); 2612 | } 2613 | else 2614 | { 2615 | // 4-byte codepoints: 000uuuuu zzzzyyyy yyxxxxxx 2616 | // repr: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 2617 | // 2618 | // 0xf0 = 11110000 2619 | // 0x07 = 00000111 2620 | // 2621 | result += static_cast(0xf0 | ((codepoint >> 18) & 0x07)); 2622 | result += static_cast(0x80 | ((codepoint >> 12) & 0x3f)); 2623 | result += static_cast(0x80 | ((codepoint >> 6) & 0x3f)); 2624 | result += static_cast(0x80 | (codepoint & 0x3f)); 2625 | } 2626 | return result; 2627 | } 2628 | 2629 | uint32_t parse_hex(std::string::iterator& it, 2630 | const std::string::iterator& end, uint32_t place) 2631 | { 2632 | uint32_t value = 0; 2633 | while (place > 0) 2634 | { 2635 | if (it == end) 2636 | throw_parse_exception("Unexpected end of unicode sequence"); 2637 | 2638 | if (!is_hex(*it)) 2639 | throw_parse_exception("Invalid unicode escape sequence"); 2640 | 2641 | value += place * hex_to_digit(*it++); 2642 | place /= 16; 2643 | } 2644 | return value; 2645 | } 2646 | 2647 | uint32_t hex_to_digit(char c) 2648 | { 2649 | if (is_number(c)) 2650 | return static_cast(c - '0'); 2651 | return 10 2652 | + static_cast(c 2653 | - ((c >= 'a' && c <= 'f') ? 'a' : 'A')); 2654 | } 2655 | 2656 | std::shared_ptr parse_number(std::string::iterator& it, 2657 | const std::string::iterator& end) 2658 | { 2659 | auto check_it = it; 2660 | auto check_end = find_end_of_number(it, end); 2661 | 2662 | auto eat_sign = [&]() { 2663 | if (check_it != end && (*check_it == '-' || *check_it == '+')) 2664 | ++check_it; 2665 | }; 2666 | 2667 | auto check_no_leading_zero = [&]() { 2668 | if (check_it != end && *check_it == '0' && check_it + 1 != check_end 2669 | && check_it[1] != '.') 2670 | { 2671 | throw_parse_exception("Numbers may not have leading zeros"); 2672 | } 2673 | }; 2674 | 2675 | auto eat_digits = [&](bool (*check_char)(char)) { 2676 | auto beg = check_it; 2677 | while (check_it != end && check_char(*check_it)) 2678 | { 2679 | ++check_it; 2680 | if (check_it != end && *check_it == '_') 2681 | { 2682 | ++check_it; 2683 | if (check_it == end || !check_char(*check_it)) 2684 | throw_parse_exception("Malformed number"); 2685 | } 2686 | } 2687 | 2688 | if (check_it == beg) 2689 | throw_parse_exception("Malformed number"); 2690 | }; 2691 | 2692 | auto eat_hex = [&]() { eat_digits(&is_hex); }; 2693 | 2694 | auto eat_numbers = [&]() { eat_digits(&is_number); }; 2695 | 2696 | if (check_it != end && *check_it == '0' && check_it + 1 != check_end 2697 | && (check_it[1] == 'x' || check_it[1] == 'o' || check_it[1] == 'b')) 2698 | { 2699 | ++check_it; 2700 | char base = *check_it; 2701 | ++check_it; 2702 | if (base == 'x') 2703 | { 2704 | eat_hex(); 2705 | return parse_int(it, check_it, 16); 2706 | } 2707 | else if (base == 'o') 2708 | { 2709 | auto start = check_it; 2710 | eat_numbers(); 2711 | auto val = parse_int(start, check_it, 8, "0"); 2712 | it = start; 2713 | return val; 2714 | } 2715 | else // if (base == 'b') 2716 | { 2717 | auto start = check_it; 2718 | eat_numbers(); 2719 | auto val = parse_int(start, check_it, 2); 2720 | it = start; 2721 | return val; 2722 | } 2723 | } 2724 | 2725 | eat_sign(); 2726 | check_no_leading_zero(); 2727 | 2728 | if (check_it != end && check_it + 1 != end && check_it + 2 != end) 2729 | { 2730 | if (check_it[0] == 'i' && check_it[1] == 'n' && check_it[2] == 'f') 2731 | { 2732 | auto val = std::numeric_limits::infinity(); 2733 | if (*it == '-') 2734 | val = -val; 2735 | it = check_it + 3; 2736 | return make_value(val); 2737 | } 2738 | else if (check_it[0] == 'n' && check_it[1] == 'a' 2739 | && check_it[2] == 'n') 2740 | { 2741 | auto val = std::numeric_limits::quiet_NaN(); 2742 | if (*it == '-') 2743 | val = -val; 2744 | it = check_it + 3; 2745 | return make_value(val); 2746 | } 2747 | } 2748 | 2749 | eat_numbers(); 2750 | 2751 | if (check_it != end 2752 | && (*check_it == '.' || *check_it == 'e' || *check_it == 'E')) 2753 | { 2754 | bool is_exp = *check_it == 'e' || *check_it == 'E'; 2755 | 2756 | ++check_it; 2757 | if (check_it == end) 2758 | throw_parse_exception("Floats must have trailing digits"); 2759 | 2760 | auto eat_exp = [&]() { 2761 | eat_sign(); 2762 | check_no_leading_zero(); 2763 | eat_numbers(); 2764 | }; 2765 | 2766 | if (is_exp) 2767 | eat_exp(); 2768 | else 2769 | eat_numbers(); 2770 | 2771 | if (!is_exp && check_it != end 2772 | && (*check_it == 'e' || *check_it == 'E')) 2773 | { 2774 | ++check_it; 2775 | eat_exp(); 2776 | } 2777 | 2778 | return parse_float(it, check_it); 2779 | } 2780 | else 2781 | { 2782 | return parse_int(it, check_it); 2783 | } 2784 | } 2785 | 2786 | std::shared_ptr> parse_int(std::string::iterator& it, 2787 | const std::string::iterator& end, 2788 | int base = 10, 2789 | const char* prefix = "") 2790 | { 2791 | std::string v{it, end}; 2792 | v = prefix + v; 2793 | v.erase(std::remove(v.begin(), v.end(), '_'), v.end()); 2794 | it = end; 2795 | try 2796 | { 2797 | return make_value(std::stoll(v, nullptr, base)); 2798 | } 2799 | catch (const std::invalid_argument& ex) 2800 | { 2801 | throw_parse_exception("Malformed number (invalid argument: " 2802 | + std::string{ex.what()} + ")"); 2803 | } 2804 | catch (const std::out_of_range& ex) 2805 | { 2806 | throw_parse_exception("Malformed number (out of range: " 2807 | + std::string{ex.what()} + ")"); 2808 | } 2809 | } 2810 | 2811 | std::shared_ptr> parse_float(std::string::iterator& it, 2812 | const std::string::iterator& end) 2813 | { 2814 | std::string v{it, end}; 2815 | v.erase(std::remove(v.begin(), v.end(), '_'), v.end()); 2816 | it = end; 2817 | char decimal_point = std::localeconv()->decimal_point[0]; 2818 | std::replace(v.begin(), v.end(), '.', decimal_point); 2819 | try 2820 | { 2821 | return make_value(std::stod(v)); 2822 | } 2823 | catch (const std::invalid_argument& ex) 2824 | { 2825 | throw_parse_exception("Malformed number (invalid argument: " 2826 | + std::string{ex.what()} + ")"); 2827 | } 2828 | catch (const std::out_of_range& ex) 2829 | { 2830 | throw_parse_exception("Malformed number (out of range: " 2831 | + std::string{ex.what()} + ")"); 2832 | } 2833 | } 2834 | 2835 | std::shared_ptr> parse_bool(std::string::iterator& it, 2836 | const std::string::iterator& end) 2837 | { 2838 | auto eat = make_consumer(it, end, [this]() { 2839 | throw_parse_exception("Attempted to parse invalid boolean value"); 2840 | }); 2841 | 2842 | if (*it == 't') 2843 | { 2844 | eat("true"); 2845 | return make_value(true); 2846 | } 2847 | else if (*it == 'f') 2848 | { 2849 | eat("false"); 2850 | return make_value(false); 2851 | } 2852 | 2853 | eat.error(); 2854 | return nullptr; 2855 | } 2856 | 2857 | std::string::iterator find_end_of_number(std::string::iterator it, 2858 | std::string::iterator end) 2859 | { 2860 | auto ret = std::find_if(it, end, [](char c) { 2861 | return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E' 2862 | && c != '-' && c != '+' && c != 'x' && c != 'o' && c != 'b'; 2863 | }); 2864 | if (ret != end && ret + 1 != end && ret + 2 != end) 2865 | { 2866 | if ((ret[0] == 'i' && ret[1] == 'n' && ret[2] == 'f') 2867 | || (ret[0] == 'n' && ret[1] == 'a' && ret[2] == 'n')) 2868 | { 2869 | ret = ret + 3; 2870 | } 2871 | } 2872 | return ret; 2873 | } 2874 | 2875 | std::string::iterator find_end_of_date(std::string::iterator it, 2876 | std::string::iterator end) 2877 | { 2878 | auto end_of_date = std::find_if(it, end, [](char c) { 2879 | return !is_number(c) && c != '-'; 2880 | }); 2881 | if (end_of_date != end && *end_of_date == ' ' && end_of_date + 1 != end 2882 | && is_number(end_of_date[1])) 2883 | end_of_date++; 2884 | return std::find_if(end_of_date, end, [](char c) { 2885 | return !is_number(c) && c != 'T' && c != 'Z' && c != ':' 2886 | && c != '-' && c != '+' && c != '.'; 2887 | }); 2888 | } 2889 | 2890 | std::string::iterator find_end_of_time(std::string::iterator it, 2891 | std::string::iterator end) 2892 | { 2893 | return std::find_if(it, end, [](char c) { 2894 | return !is_number(c) && c != ':' && c != '.'; 2895 | }); 2896 | } 2897 | 2898 | local_time read_time(std::string::iterator& it, 2899 | const std::string::iterator& end) 2900 | { 2901 | auto time_end = find_end_of_time(it, end); 2902 | 2903 | auto eat = make_consumer( 2904 | it, time_end, [&]() { throw_parse_exception("Malformed time"); }); 2905 | 2906 | local_time ltime; 2907 | 2908 | ltime.hour = eat.eat_digits(2); 2909 | eat(':'); 2910 | ltime.minute = eat.eat_digits(2); 2911 | eat(':'); 2912 | ltime.second = eat.eat_digits(2); 2913 | 2914 | int power = 100000; 2915 | if (it != time_end && *it == '.') 2916 | { 2917 | ++it; 2918 | while (it != time_end && is_number(*it)) 2919 | { 2920 | ltime.microsecond += power * (*it++ - '0'); 2921 | power /= 10; 2922 | } 2923 | } 2924 | 2925 | if (it != time_end) 2926 | throw_parse_exception("Malformed time"); 2927 | 2928 | return ltime; 2929 | } 2930 | 2931 | std::shared_ptr> 2932 | parse_time(std::string::iterator& it, const std::string::iterator& end) 2933 | { 2934 | return make_value(read_time(it, end)); 2935 | } 2936 | 2937 | std::shared_ptr parse_date(std::string::iterator& it, 2938 | const std::string::iterator& end) 2939 | { 2940 | auto date_end = find_end_of_date(it, end); 2941 | 2942 | auto eat = make_consumer( 2943 | it, date_end, [&]() { throw_parse_exception("Malformed date"); }); 2944 | 2945 | local_date ldate; 2946 | ldate.year = eat.eat_digits(4); 2947 | eat('-'); 2948 | ldate.month = eat.eat_digits(2); 2949 | eat('-'); 2950 | ldate.day = eat.eat_digits(2); 2951 | 2952 | if (it == date_end) 2953 | return make_value(ldate); 2954 | 2955 | eat.eat_or('T', ' '); 2956 | 2957 | local_datetime ldt; 2958 | static_cast(ldt) = ldate; 2959 | static_cast(ldt) = read_time(it, date_end); 2960 | 2961 | if (it == date_end) 2962 | return make_value(ldt); 2963 | 2964 | offset_datetime dt; 2965 | static_cast(dt) = ldt; 2966 | 2967 | int hoff = 0; 2968 | int moff = 0; 2969 | if (*it == '+' || *it == '-') 2970 | { 2971 | auto plus = *it == '+'; 2972 | ++it; 2973 | 2974 | hoff = eat.eat_digits(2); 2975 | dt.hour_offset = (plus) ? hoff : -hoff; 2976 | eat(':'); 2977 | moff = eat.eat_digits(2); 2978 | dt.minute_offset = (plus) ? moff : -moff; 2979 | } 2980 | else if (*it == 'Z') 2981 | { 2982 | ++it; 2983 | } 2984 | 2985 | if (it != date_end) 2986 | throw_parse_exception("Malformed date"); 2987 | 2988 | return make_value(dt); 2989 | } 2990 | 2991 | std::shared_ptr parse_array(std::string::iterator& it, 2992 | std::string::iterator& end) 2993 | { 2994 | // this gets ugly because of the "homogeneity" restriction: 2995 | // arrays can either be of only one type, or contain arrays 2996 | // (each of those arrays could be of different types, though) 2997 | // 2998 | // because of the latter portion, we don't really have a choice 2999 | // but to represent them as arrays of base values... 3000 | ++it; 3001 | 3002 | // ugh---have to read the first value to determine array type... 3003 | skip_whitespace_and_comments(it, end); 3004 | 3005 | // edge case---empty array 3006 | if (*it == ']') 3007 | { 3008 | ++it; 3009 | return make_array(); 3010 | } 3011 | 3012 | auto val_end = std::find_if( 3013 | it, end, [](char c) { return c == ',' || c == ']' || c == '#'; }); 3014 | parse_type type = determine_value_type(it, val_end); 3015 | switch (type) 3016 | { 3017 | case parse_type::STRING: 3018 | return parse_value_array(it, end); 3019 | case parse_type::LOCAL_TIME: 3020 | return parse_value_array(it, end); 3021 | case parse_type::LOCAL_DATE: 3022 | return parse_value_array(it, end); 3023 | case parse_type::LOCAL_DATETIME: 3024 | return parse_value_array(it, end); 3025 | case parse_type::OFFSET_DATETIME: 3026 | return parse_value_array(it, end); 3027 | case parse_type::INT: 3028 | return parse_value_array(it, end); 3029 | case parse_type::FLOAT: 3030 | return parse_value_array(it, end); 3031 | case parse_type::BOOL: 3032 | return parse_value_array(it, end); 3033 | case parse_type::ARRAY: 3034 | return parse_object_array(&parser::parse_array, '[', it, 3035 | end); 3036 | case parse_type::INLINE_TABLE: 3037 | return parse_object_array( 3038 | &parser::parse_inline_table, '{', it, end); 3039 | default: 3040 | throw_parse_exception("Unable to parse array"); 3041 | } 3042 | } 3043 | 3044 | template 3045 | std::shared_ptr parse_value_array(std::string::iterator& it, 3046 | std::string::iterator& end) 3047 | { 3048 | auto arr = make_array(); 3049 | while (it != end && *it != ']') 3050 | { 3051 | auto val = parse_value(it, end); 3052 | if (auto v = val->as()) 3053 | arr->get().push_back(val); 3054 | else 3055 | throw_parse_exception("Arrays must be homogeneous"); 3056 | skip_whitespace_and_comments(it, end); 3057 | if (*it != ',') 3058 | break; 3059 | ++it; 3060 | skip_whitespace_and_comments(it, end); 3061 | } 3062 | if (it != end) 3063 | ++it; 3064 | return arr; 3065 | } 3066 | 3067 | template 3068 | std::shared_ptr parse_object_array(Function&& fun, char delim, 3069 | std::string::iterator& it, 3070 | std::string::iterator& end) 3071 | { 3072 | auto arr = detail::make_element(); 3073 | 3074 | while (it != end && *it != ']') 3075 | { 3076 | if (*it != delim) 3077 | throw_parse_exception("Unexpected character in array"); 3078 | 3079 | arr->get().push_back(((*this).*fun)(it, end)); 3080 | skip_whitespace_and_comments(it, end); 3081 | 3082 | if (it == end || *it != ',') 3083 | break; 3084 | 3085 | ++it; 3086 | skip_whitespace_and_comments(it, end); 3087 | } 3088 | 3089 | if (it == end || *it != ']') 3090 | throw_parse_exception("Unterminated array"); 3091 | 3092 | ++it; 3093 | return arr; 3094 | } 3095 | 3096 | std::shared_ptr
parse_inline_table(std::string::iterator& it, 3097 | std::string::iterator& end) 3098 | { 3099 | auto tbl = make_table(); 3100 | do 3101 | { 3102 | ++it; 3103 | if (it == end) 3104 | throw_parse_exception("Unterminated inline table"); 3105 | 3106 | consume_whitespace(it, end); 3107 | if (it != end && *it != '}') 3108 | { 3109 | parse_key_value(it, end, tbl.get()); 3110 | consume_whitespace(it, end); 3111 | } 3112 | } while (*it == ','); 3113 | 3114 | if (it == end || *it != '}') 3115 | throw_parse_exception("Unterminated inline table"); 3116 | 3117 | ++it; 3118 | consume_whitespace(it, end); 3119 | 3120 | return tbl; 3121 | } 3122 | 3123 | void skip_whitespace_and_comments(std::string::iterator& start, 3124 | std::string::iterator& end) 3125 | { 3126 | consume_whitespace(start, end); 3127 | while (start == end || *start == '#') 3128 | { 3129 | if (!detail::getline(input_, line_)) 3130 | throw_parse_exception("Unclosed array"); 3131 | line_number_++; 3132 | start = line_.begin(); 3133 | end = line_.end(); 3134 | consume_whitespace(start, end); 3135 | } 3136 | } 3137 | 3138 | void consume_whitespace(std::string::iterator& it, 3139 | const std::string::iterator& end) 3140 | { 3141 | while (it != end && (*it == ' ' || *it == '\t')) 3142 | ++it; 3143 | } 3144 | 3145 | void consume_backwards_whitespace(std::string::iterator& back, 3146 | const std::string::iterator& front) 3147 | { 3148 | while (back != front && (*back == ' ' || *back == '\t')) 3149 | --back; 3150 | } 3151 | 3152 | void eol_or_comment(const std::string::iterator& it, 3153 | const std::string::iterator& end) 3154 | { 3155 | if (it != end && *it != '#') 3156 | throw_parse_exception("Unidentified trailing character '" 3157 | + std::string{*it} 3158 | + "'---did you forget a '#'?"); 3159 | } 3160 | 3161 | bool is_time(const std::string::iterator& it, 3162 | const std::string::iterator& end) 3163 | { 3164 | auto time_end = find_end_of_time(it, end); 3165 | auto len = std::distance(it, time_end); 3166 | 3167 | if (len < 8) 3168 | return false; 3169 | 3170 | if (it[2] != ':' || it[5] != ':') 3171 | return false; 3172 | 3173 | if (len > 8) 3174 | return it[8] == '.' && len > 9; 3175 | 3176 | return true; 3177 | } 3178 | 3179 | option date_type(const std::string::iterator& it, 3180 | const std::string::iterator& end) 3181 | { 3182 | auto date_end = find_end_of_date(it, end); 3183 | auto len = std::distance(it, date_end); 3184 | 3185 | if (len < 10) 3186 | return {}; 3187 | 3188 | if (it[4] != '-' || it[7] != '-') 3189 | return {}; 3190 | 3191 | if (len >= 19 && (it[10] == 'T' || it[10] == ' ') 3192 | && is_time(it + 11, date_end)) 3193 | { 3194 | // datetime type 3195 | auto time_end = find_end_of_time(it + 11, date_end); 3196 | if (time_end == date_end) 3197 | return {parse_type::LOCAL_DATETIME}; 3198 | else 3199 | return {parse_type::OFFSET_DATETIME}; 3200 | } 3201 | else if (len == 10) 3202 | { 3203 | // just a regular date 3204 | return {parse_type::LOCAL_DATE}; 3205 | } 3206 | 3207 | return {}; 3208 | } 3209 | 3210 | std::istream& input_; 3211 | std::string line_; 3212 | std::size_t line_number_ = 0; 3213 | }; 3214 | 3215 | /** 3216 | * Utility function to parse a file as a TOML file. Returns the root table. 3217 | * Throws a parse_exception if the file cannot be opened. 3218 | */ 3219 | inline std::shared_ptr
parse_file(const std::string& filename) 3220 | { 3221 | #if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP) 3222 | boost::nowide::ifstream file{filename.c_str()}; 3223 | #elif defined(NOWIDE_FSTREAM_INCLUDED_HPP) 3224 | nowide::ifstream file{filename.c_str()}; 3225 | #else 3226 | std::ifstream file{filename}; 3227 | #endif 3228 | if (!file.is_open()) 3229 | throw parse_exception{filename + " could not be opened for parsing"}; 3230 | parser p{file}; 3231 | return p.parse(); 3232 | } 3233 | 3234 | template 3235 | struct value_accept; 3236 | 3237 | template <> 3238 | struct value_accept<> 3239 | { 3240 | template 3241 | static void accept(const base&, Visitor&&, Args&&...) 3242 | { 3243 | // nothing 3244 | } 3245 | }; 3246 | 3247 | template 3248 | struct value_accept 3249 | { 3250 | template 3251 | static void accept(const base& b, Visitor&& visitor, Args&&... args) 3252 | { 3253 | if (auto v = b.as()) 3254 | { 3255 | visitor.visit(*v, std::forward(args)...); 3256 | } 3257 | else 3258 | { 3259 | value_accept::accept(b, std::forward(visitor), 3260 | std::forward(args)...); 3261 | } 3262 | } 3263 | }; 3264 | 3265 | /** 3266 | * base implementation of accept() that calls visitor.visit() on the concrete 3267 | * class. 3268 | */ 3269 | template 3270 | void base::accept(Visitor&& visitor, Args&&... args) const 3271 | { 3272 | if (is_value()) 3273 | { 3274 | using value_acceptor 3275 | = value_accept; 3277 | value_acceptor::accept(*this, std::forward(visitor), 3278 | std::forward(args)...); 3279 | } 3280 | else if (is_table()) 3281 | { 3282 | visitor.visit(static_cast(*this), 3283 | std::forward(args)...); 3284 | } 3285 | else if (is_array()) 3286 | { 3287 | visitor.visit(static_cast(*this), 3288 | std::forward(args)...); 3289 | } 3290 | else if (is_table_array()) 3291 | { 3292 | visitor.visit(static_cast(*this), 3293 | std::forward(args)...); 3294 | } 3295 | } 3296 | 3297 | /** 3298 | * Writer that can be passed to accept() functions of cpptoml objects and 3299 | * will output valid TOML to a stream. 3300 | */ 3301 | class toml_writer 3302 | { 3303 | public: 3304 | /** 3305 | * Construct a toml_writer that will write to the given stream 3306 | */ 3307 | toml_writer(std::ostream& s, const std::string& indent_space = "\t") 3308 | : stream_(s), indent_(indent_space), has_naked_endline_(false) 3309 | { 3310 | // nothing 3311 | } 3312 | 3313 | public: 3314 | /** 3315 | * Output a base value of the TOML tree. 3316 | */ 3317 | template 3318 | void visit(const value& v, bool = false) 3319 | { 3320 | write(v); 3321 | } 3322 | 3323 | /** 3324 | * Output a table element of the TOML tree 3325 | */ 3326 | void visit(const table& t, bool in_array = false) 3327 | { 3328 | write_table_header(in_array); 3329 | std::vector values; 3330 | std::vector tables; 3331 | 3332 | for (const auto& i : t) 3333 | { 3334 | if (i.second->is_table() || i.second->is_table_array()) 3335 | { 3336 | tables.push_back(i.first); 3337 | } 3338 | else 3339 | { 3340 | values.push_back(i.first); 3341 | } 3342 | } 3343 | 3344 | for (unsigned int i = 0; i < values.size(); ++i) 3345 | { 3346 | path_.push_back(values[i]); 3347 | 3348 | if (i > 0) 3349 | endline(); 3350 | 3351 | write_table_item_header(*t.get(values[i])); 3352 | t.get(values[i])->accept(*this, false); 3353 | path_.pop_back(); 3354 | } 3355 | 3356 | for (unsigned int i = 0; i < tables.size(); ++i) 3357 | { 3358 | path_.push_back(tables[i]); 3359 | 3360 | if (values.size() > 0 || i > 0) 3361 | endline(); 3362 | 3363 | write_table_item_header(*t.get(tables[i])); 3364 | t.get(tables[i])->accept(*this, false); 3365 | path_.pop_back(); 3366 | } 3367 | 3368 | endline(); 3369 | } 3370 | 3371 | /** 3372 | * Output an array element of the TOML tree 3373 | */ 3374 | void visit(const array& a, bool = false) 3375 | { 3376 | write("["); 3377 | 3378 | for (unsigned int i = 0; i < a.get().size(); ++i) 3379 | { 3380 | if (i > 0) 3381 | write(", "); 3382 | 3383 | if (a.get()[i]->is_array()) 3384 | { 3385 | a.get()[i]->as_array()->accept(*this, true); 3386 | } 3387 | else 3388 | { 3389 | a.get()[i]->accept(*this, true); 3390 | } 3391 | } 3392 | 3393 | write("]"); 3394 | } 3395 | 3396 | /** 3397 | * Output a table_array element of the TOML tree 3398 | */ 3399 | void visit(const table_array& t, bool = false) 3400 | { 3401 | for (unsigned int j = 0; j < t.get().size(); ++j) 3402 | { 3403 | if (j > 0) 3404 | endline(); 3405 | 3406 | t.get()[j]->accept(*this, true); 3407 | } 3408 | 3409 | endline(); 3410 | } 3411 | 3412 | /** 3413 | * Escape a string for output. 3414 | */ 3415 | static std::string escape_string(const std::string& str) 3416 | { 3417 | std::string res; 3418 | for (auto it = str.begin(); it != str.end(); ++it) 3419 | { 3420 | if (*it == '\b') 3421 | { 3422 | res += "\\b"; 3423 | } 3424 | else if (*it == '\t') 3425 | { 3426 | res += "\\t"; 3427 | } 3428 | else if (*it == '\n') 3429 | { 3430 | res += "\\n"; 3431 | } 3432 | else if (*it == '\f') 3433 | { 3434 | res += "\\f"; 3435 | } 3436 | else if (*it == '\r') 3437 | { 3438 | res += "\\r"; 3439 | } 3440 | else if (*it == '"') 3441 | { 3442 | res += "\\\""; 3443 | } 3444 | else if (*it == '\\') 3445 | { 3446 | res += "\\\\"; 3447 | } 3448 | else if (static_cast(*it) <= UINT32_C(0x001f)) 3449 | { 3450 | res += "\\u"; 3451 | std::stringstream ss; 3452 | ss << std::hex << static_cast(*it); 3453 | res += ss.str(); 3454 | } 3455 | else 3456 | { 3457 | res += *it; 3458 | } 3459 | } 3460 | return res; 3461 | } 3462 | 3463 | protected: 3464 | /** 3465 | * Write out a string. 3466 | */ 3467 | void write(const value& v) 3468 | { 3469 | write("\""); 3470 | write(escape_string(v.get())); 3471 | write("\""); 3472 | } 3473 | 3474 | /** 3475 | * Write out a double. 3476 | */ 3477 | void write(const value& v) 3478 | { 3479 | std::stringstream ss; 3480 | ss << std::showpoint 3481 | << std::setprecision(std::numeric_limits::max_digits10) 3482 | << v.get(); 3483 | 3484 | auto double_str = ss.str(); 3485 | auto pos = double_str.find("e0"); 3486 | if (pos != std::string::npos) 3487 | double_str.replace(pos, 2, "e"); 3488 | pos = double_str.find("e-0"); 3489 | if (pos != std::string::npos) 3490 | double_str.replace(pos, 3, "e-"); 3491 | 3492 | stream_ << double_str; 3493 | has_naked_endline_ = false; 3494 | } 3495 | 3496 | /** 3497 | * Write out an integer, local_date, local_time, local_datetime, or 3498 | * offset_datetime. 3499 | */ 3500 | template 3501 | typename std::enable_if< 3502 | is_one_of::value>::type 3504 | write(const value& v) 3505 | { 3506 | write(v.get()); 3507 | } 3508 | 3509 | /** 3510 | * Write out a boolean. 3511 | */ 3512 | void write(const value& v) 3513 | { 3514 | write((v.get() ? "true" : "false")); 3515 | } 3516 | 3517 | /** 3518 | * Write out the header of a table. 3519 | */ 3520 | void write_table_header(bool in_array = false) 3521 | { 3522 | if (!path_.empty()) 3523 | { 3524 | indent(); 3525 | 3526 | write("["); 3527 | 3528 | if (in_array) 3529 | { 3530 | write("["); 3531 | } 3532 | 3533 | for (unsigned int i = 0; i < path_.size(); ++i) 3534 | { 3535 | if (i > 0) 3536 | { 3537 | write("."); 3538 | } 3539 | 3540 | if (path_[i].find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" 3541 | "fghijklmnopqrstuvwxyz0123456789" 3542 | "_-") 3543 | == std::string::npos) 3544 | { 3545 | write(path_[i]); 3546 | } 3547 | else 3548 | { 3549 | write("\""); 3550 | write(escape_string(path_[i])); 3551 | write("\""); 3552 | } 3553 | } 3554 | 3555 | if (in_array) 3556 | { 3557 | write("]"); 3558 | } 3559 | 3560 | write("]"); 3561 | endline(); 3562 | } 3563 | } 3564 | 3565 | /** 3566 | * Write out the identifier for an item in a table. 3567 | */ 3568 | void write_table_item_header(const base& b) 3569 | { 3570 | if (!b.is_table() && !b.is_table_array()) 3571 | { 3572 | indent(); 3573 | 3574 | if (path_.back().find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde" 3575 | "fghijklmnopqrstuvwxyz0123456789" 3576 | "_-") 3577 | == std::string::npos) 3578 | { 3579 | write(path_.back()); 3580 | } 3581 | else 3582 | { 3583 | write("\""); 3584 | write(escape_string(path_.back())); 3585 | write("\""); 3586 | } 3587 | 3588 | write(" = "); 3589 | } 3590 | } 3591 | 3592 | private: 3593 | /** 3594 | * Indent the proper number of tabs given the size of 3595 | * the path. 3596 | */ 3597 | void indent() 3598 | { 3599 | for (std::size_t i = 1; i < path_.size(); ++i) 3600 | write(indent_); 3601 | } 3602 | 3603 | /** 3604 | * Write a value out to the stream. 3605 | */ 3606 | template 3607 | void write(const T& v) 3608 | { 3609 | stream_ << v; 3610 | has_naked_endline_ = false; 3611 | } 3612 | 3613 | /** 3614 | * Write an endline out to the stream 3615 | */ 3616 | void endline() 3617 | { 3618 | if (!has_naked_endline_) 3619 | { 3620 | stream_ << "\n"; 3621 | has_naked_endline_ = true; 3622 | } 3623 | } 3624 | 3625 | private: 3626 | std::ostream& stream_; 3627 | const std::string indent_; 3628 | std::vector path_; 3629 | bool has_naked_endline_; 3630 | }; 3631 | 3632 | inline std::ostream& operator<<(std::ostream& stream, const base& b) 3633 | { 3634 | toml_writer writer{stream}; 3635 | b.accept(writer); 3636 | return stream; 3637 | } 3638 | 3639 | template 3640 | std::ostream& operator<<(std::ostream& stream, const value& v) 3641 | { 3642 | toml_writer writer{stream}; 3643 | v.accept(writer); 3644 | return stream; 3645 | } 3646 | 3647 | inline std::ostream& operator<<(std::ostream& stream, const table& t) 3648 | { 3649 | toml_writer writer{stream}; 3650 | t.accept(writer); 3651 | return stream; 3652 | } 3653 | 3654 | inline std::ostream& operator<<(std::ostream& stream, const table_array& t) 3655 | { 3656 | toml_writer writer{stream}; 3657 | t.accept(writer); 3658 | return stream; 3659 | } 3660 | 3661 | inline std::ostream& operator<<(std::ostream& stream, const array& a) 3662 | { 3663 | toml_writer writer{stream}; 3664 | a.accept(writer); 3665 | return stream; 3666 | } 3667 | } // namespace cpptoml 3668 | #endif // CPPTOML_H 3669 | -------------------------------------------------------------------------------- /travis/install_libcxx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -v 3 | cwd=$(pwd) 4 | 5 | LLVM_TAG="${LLVM_TAG:-RELEASE_381}" 6 | 7 | svn co --quiet http://llvm.org/svn/llvm-project/llvm/tags/$LLVM_TAG/final llvm 8 | 9 | cd llvm/projects 10 | svn co --quiet http://llvm.org/svn/llvm-project/libcxx/tags/$LLVM_TAG/final libcxx 11 | svn co --quiet http://llvm.org/svn/llvm-project/libcxxabi/tags/$LLVM_TAG/final libcxxabi 12 | cd ../ 13 | 14 | mkdir build 15 | cd build 16 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$HOME ../ 17 | make cxx 18 | make install-libcxx install-libcxxabi 19 | 20 | cd $cwd 21 | set +v 22 | --------------------------------------------------------------------------------